Como um grafo completo encontra chamadas ocultas
Um grafo de dependências do código completo revela chamadas entre linguagens, nomes gerados, passagens batch e contratos de dados ignorados.

Um grafo de dependências só é tão honesto quanto o limite usado para construí-lo. Examinar um repositório por vez pode produzir um resultado preciso, completo e quase inútil: as chamadas que decidem se uma reescrita preserva o comportamento muitas vezes atravessam um agendador, um banco de dados, um arquivo gerado, um copybook compartilhado ou um protocolo que não pertence a repositório algum.
Ler a árvore inteira em conjunto muda a unidade de análise. Um programa COBOL e seu JCL deixam de ser ativos separados. Um job RPG, um wrapper CL e uma tabela gravada por uma importação noturna tornam-se um único caminho de execução. O grafo liga evidências que vivem em linguagens diferentes e marca onde elas terminam. Isso importa: um grafo confiável distingue uma aresta comprovada de uma plausível, em vez de tratar todo nome parecido como certeza.
Já vi análises por repositório produzirem diagramas tranquilizadores enquanto o caminho de produção passava por código que o desenho nunca tocava. O analisador não estava quebrado. A pergunta era pequena demais.
Os limites dos repositórios são administrativos
Um grafo por repositório descreve como arquivos-fonte fazem referência uns aos outros dentro de um contêiner escolhido. Um grafo do sistema descreve como o comportamento percorre todos os artefatos capazes de influenciar uma execução. São produtos diferentes, e confundi-los cria escopos de migração incompletos.
Repositórios geralmente refletem propriedade das equipes, regras de acesso, entregas de fornecedores ou uma migração antiga. O comportamento em execução não respeita essas escolhas. Uma requisição web pode começar em Classic ASP, chamar um componente COM criado em VB6, executar uma stored procedure mantida no repositório do banco e deixar trabalho para um programa COBOL agendado. Cada repositório pode ser bem compreendido enquanto a transação de negócio continua invisível.
A palavra dependência também exige disciplina. Uma referência textual comprova que um artefato nomeia outro. Uma dependência chamável significa que o alvo pode receber o controle sob alguma condição. Uma dependência comportamental significa que alterar o alvo pode mudar um resultado observado por usuários ou sistemas posteriores. Ferramentas locais frequentemente reduzem as três relações à mesma linha. Numa reescrita, esse atalho gera confiança falsa e trabalho desperdiçado.
O grafo prático precisa de mais que arquivos e funções como nós. Deve incluir jobs, objetos de banco, destinos de mensagens, artefatos gerados, programas externos, esquemas, telas, relatórios e configurações quando participam do comportamento. Cada aresta precisa registrar sua razão: chamada direta, seleção dinâmica, fluxo de dados, agendamento, geração ou nome não resolvido. Com essa evidência, um engenheiro pode contestar o grafo em vez de apenas admirá-lo.
Esse modelo amplo não exige colocar tudo num só repositório. Mantenha os repositórios e mude o limite da análise. O limite correto é o conjunto de artefatos que pode influenciar o comportamento reescrito, mesmo quando propriedade e implantação os separam.
Arestas entre linguagens usam mecanismos comuns
As chamadas importantes raramente se apresentam como arquitetura multilinguagem. Elas aparecem como mecanismos normais da plataforma: string de comando, nome de stored procedure, etapa de job, símbolo exportado, destino de fila ou nome de arquivo produzido para o processo seguinte. Um parser de linguagem vê sintaxe. Um grafo de sistema interpreta o que essa sintaxe causa em outro lugar.
Considere uma etapa JCL com PGM=BILLRUN. A aresta não termina no token BILLRUN. A análise deve resolver o programa pelas bibliotecas de carga relevantes, ligá-lo ao inventário de fontes ou binários e preservar a incerteza quando há vários candidatos. Dentro do programa, um CALL WS-PROGRAM em COBOL não é uma chamada estática comum porque o alvo vem de dados. O valor pode vir de um copybook, arquivo de parâmetros ou leitura anterior do banco.
O mesmo padrão aparece em outras plataformas. CL pode enviar um programa RPG para uma fila batch. VB6 pode criar uma classe COM usando uma string do registro ou da configuração. ColdFusion pode chamar um procedimento implementado em PL/SQL. Um monólito PHP pode gravar um arquivo de controle que um daemon Perl interpreta como instrução. Nada disso exige reflexão exótica. As arestas desaparecem porque cada parser para no limite de sua linguagem ou repositório.
Um artefato intermediário útil torna a ligação visível e revisável. Um arquivo de arestas separado por tabulações pode carregar a evidência mínima:
source target mechanism evidence
JCL:AR_CLOSE:STEP20 COBOL:BILLRUN program-load PGM=BILLRUN
COBOL:BILLRUN:PARA140 DB2:SP_POST_LEDGER dynamic-sql value-set:POST_LEDGER
DB2:SP_POST_LEDGER TABLE:GL_ENTRY write INSERT INTO GL_ENTRY
CL:ENDDAY:CMD7 RPG:RECONCILE submit-job CALL PGM(RECONCILE)
Esse arquivo não é o grafo final. É um contrato entre extração e revisão. Cada linha declara origem, destino, mecanismo e evidência. Se a ferramenta não consegue preencher a última coluna, não deve desenhar em silêncio uma aresta firme.
Os nomes se resolvem quando as evidências se juntam
Chamadas dinâmicas não são necessariamente impossíveis de conhecer. Muitas tornam-se resolúveis quando a análise reúne atribuições, configuração, metadados de compilação e amostras de execução da árvore inteira. O erro é tratar um ponto de chamada não literal como beco sem saída antes de procurar os valores que podem alcançá-lo.
Suponha que um parágrafo COBOL chame WS-NEXT-PGM. Um repositório contém a chamada, mas não a atribuição. JCL em outro repositório passa um parâmetro simbólico. Um copybook compartilhado define a largura do campo. Uma exportação da tabela de controle contém os valores implantados. Separadamente, a chamada não se resolve. Em conjunto, a análise deriva candidatos, rejeita valores que não cabem e liga os restantes ao inventário de programas.
A resolução deve continuar baseada em evidências. Nas revisões, uso quatro estados de aresta: confirmada pela sintaxe, derivada de valores limitados, observada no tráfego gravado e não resolvida. Os nomes importam menos que manter os estados separados. Um candidato derivado ajuda no escopo, mas não prova que produção percorra aquele caminho. Uma chamada observada prova ocorrência na amostra, não a inexistência de outros alvos.
É aqui que a comparação ingênua de nomes também falha. POST, UPDATE ou CLOSE podem aparecer em dezenas de espaços. Uma correspondência só ganha credibilidade depois de aplicar regras de resolução da plataforma, restrições do campo, convenção de chamada, contexto de implantação e atribuições alcançáveis. A análise global fornece mais evidências, mas também precisa de desambiguação mais rígida. Mais entradas sem regras apenas criam um grafo errado e mais denso.
Código gerado merece o mesmo tratamento. Gerador, modelos, entradas e artefato emitido formam uma cadeia. Examinar apenas a saída faz o grafo confundir consequência com autoridade. Examinar apenas os modelos perde os nomes concretos gerados para uma implantação. Mantenha ambos e marque a aresta de geração, para rastrear uma mudança até o item que recriará o arquivo.
O movimento dos dados costuma ser a chamada ausente
Um grafo de chamadas não explica muitos sistemas legados porque os dados fazem a passagem. Um processo grava uma linha, arquivo ou entrada de spool; outro interpreta isso depois. Nenhuma função chama diretamente a próxima, mas o primeiro programa controla o que o segundo faz. Para paridade comportamental, isso é dependência.
O processamento noturno deixa isso claro. Uma transação online grava um status numa tabela. Um agendador inicia um job batch no fechamento. O job seleciona linhas com aquele status, cria um arquivo de largura fixa e outro programa importa o arquivo no razão contábil. Grafos locais mostram quatro ilhas. O grafo do sistema mostra uma transação com predicado, agenda, layout de registro e regra de nome de arquivo.
Tratar toda tabela compartilhada como dependência criaria ruído. O grafo precisa considerar operação e campos. Um gravador que altera customer.last_seen pode não influenciar um leitor que filtra customer.credit_hold. Um gravador que muda o campo de status filtrado influencia. Dois programas que tocam o mesmo arquivo também não estão conectados se usam tipos de registro diferentes.
Uma aresta de dados útil registra pelo menos objeto, operação, campos ou layout relevantes e o predicado que ativa o leitor. Para arquivos batch, inclua produtor, consumidor, regra de nome, codificação, delimitador ou posições e totais de controle verificados. Em stored procedures, diferencie a chamada do procedimento da leitura de tabelas que ele altera. Esses detalhes definem onde a reescrita pode introduzir com segurança um novo esquema ou limite de serviço.
A pergunta incômoda é se isso torna o grafo amplo demais. Torna, se toda coincidência de dados virar aresta firme. Ele continua útil quando oferece vistas filtradas: transferência de controle, influência dos dados, agendamento, geração e incerteza externa. O modelo unificado deve preservar os tipos de aresta, não achatá-los numa massa de setas.
Mesmo uma árvore completa tem um exterior
Ler cada arquivo fornecido não produz conhecimento completo do sistema em execução. Produz conhecimento completo daquele conjunto de evidências. Agendadores externos, triggers, comandos de operador, entradas de registro, roteamento de middleware, binários de fornecedores e configuração de produção podem introduzir comportamento que a análise de fonte não comprova.
Muitos relatórios de modernização tornam-se desonestos nessa distinção. Chamam o grafo de completo quando apenas o scanner terminou. Concluir a varredura não diz se a entrada cobria o ambiente de execução. Um resultado forte inclui uma fronteira explícita: nós e arestas que apontam para além das evidências, com o motivo de cada questão aberta.
Torne essa fronteira concreta. Exporte nomes de programas não resolvidos, objetos externos, destinos de fila, caminhos e chaves de configuração para uma tabela de revisão. Dê a cada item um responsável e uma situação como entrega posterior, externo verificado, desativado ou ainda desconhecido. Não apague um nó porque ninguém o reconhece. Sistemas antigos contêm código inativo, mas reconhecimento não é evidência de alcançabilidade.
Observações em execução ajudam, desde que ninguém as chame de exaustivas. Tráfego de produção gravado confirma que um caminho ocorreu e fornece valores para alvos dinâmicos. Não prova que uma rara ramificação de fim de ano, um procedimento de recuperação ou um comando exclusivo de operador nunca execute. Evidência estática dá a estrutura possível; evidência de execução dá o comportamento testemunhado. A sobreposição é forte, e a divergência merece investigação.
O grafo deve responder separadamente a duas perguntas: o que os artefatos fornecidos podem causar e o que a carga gravada realmente causou? Um escopo baseado apenas na primeira visão pode preservar caminhos mortos para sempre. Um baseado apenas na segunda pode excluir uma exceção válida. Mantenha ambas e deixe a decisão visível.
Uma aresta perdida pode invalidar uma boa reescrita
Uma dependência perdida costuma falhar longe do código omitido. Esse atraso explica por que equipes subestimam os limites na fase de planejamento e culpam os testes quando a entrada em produção revela o sistema real.
Considere um fechamento mensal montado em quatro repositórios. Um programa RPG marca contas elegíveis e chama um wrapper CL. O wrapper envia um job usando um nome lido de uma área de dados. JCL em outro host executa o programa COBOL selecionado. Ele grava um arquivo de exceções de largura fixa, e uma ferramenta VB6 permite ao operador aprovar registros antes que PL/SQL os contabilize. Cada repositório tem testes e cada equipe entende seu trecho.
A análise local encontra a chamada RPG para CL e as gravações PL/SQL. Perde o programa enviado porque seu nome vem de dados, o JCL porque está em outro lugar e a aprovação no desktop porque o arquivo é a interface. A reescrita substitui o primeiro trecho por um serviço e reproduz suas atualizações no banco. Testes automáticos com contas normais passam.
No fechamento, as exceções ficam sem contabilização. O novo serviço não emite o arquivo porque ninguém incluiu essa saída no contrato. A ferramenta não mostra nada, o operador não pode aprovar e nenhuma exceção alcança o procedimento. O defeito parece de interface ou banco, mas a omissão ocorreu quando a análise desenhou o serviço ao redor de um repositório.
Um grafo global ligaria chamada, job enviado, programa agendado, produção de arquivo, ação do operador e chamada da stored procedure. Também identificaria a área de dados e a regra de nome como entradas de controle. Os testes poderiam gravar tráfego representativo nos caminhos normal e excepcional e comparar saídas em cada fronteira.
A lição não é preservar todo artefato antigo. É compreender antes de excluir. Com o caminho visível, a equipe pode substituir o arquivo e a aprovação por um cliente TypeScript e uma API de serviço. Isso é mudança de arquitetura com obrigação comportamental conhecida, não remoção acidental.
Transliterar o grafo preserva limites errados
Gerar um módulo novo para cada programa antigo parece seguro porque o mapeamento é fácil de auditar. Também conserva décadas de acidentes de implantação. O grafo deve preservar obrigações e revelar limites melhores, não ditar uma tradução arquivo a arquivo.
Limites antigos muitas vezes vieram de memória, janelas batch, restrições de linguagem ou história das equipes. Uma etapa JCL pode apenas mover dados entre layouts porque nenhum programa vizinho suportava ambos. Uma stored procedure pode guardar regras de negócio porque o cliente original era difícil de implantar. Um copybook pode acoplar programas sem relação porque era o único meio prático de distribuição. Recriar cada artefato em outra linguagem mantém restrições sem manter o motivo.
Use o grafo para encontrar comportamento coeso. Nós que mudam juntos, compartilham regras transacionais e participam do mesmo resultado podem formar um componente moderno. Arestas que cruzam zonas de confiança, ciclos de entrega independentes ou cargas realmente distintas podem virar interfaces explícitas. Arestas de dados mostram onde Postgres deve proteger invariantes. Núcleos numéricos exigentes podem justificar Rust, enquanto a orquestração fica em Go e o trabalho do operador em TypeScript. O destino segue comportamento e restrições, não extensões.
Desaconselho começar por uma conversão repositório a repositório mesmo quando compras e equipes a tornam atraente. Ela produz progresso inicial fácil de contar, mas adia comportamento transversal até a integração, quando mudar limites custa mais. Construa primeiro o grafo do sistema, escolha as novas fronteiras e só depois distribua os pacotes de trabalho.
A rastreabilidade continua necessária. Cada componente novo deve apontar para os comportamentos e arestas que substitui. Esse mapa permite perguntar se uma aresta antiga foi preservada, redesenhada de propósito ou retirada com evidências. Sem ele, modernização vira debate sobre semelhança de código, a medida errada.
Testes de paridade devem seguir caminhos
Testes de repositório são evidências úteis, mas raramente estabelecem paridade do sistema porque suas verificações param nos limites locais. Um harness de paridade deve reproduzir transações completas e comparar efeitos observáveis ao longo do caminho revelado pelo grafo.
Escolha caminhos comportamentalmente distintos, não apenas entradas frequentes. Inclua processamento normal, alvo dinâmico, passagem batch, exceção mediada por operador e falha ou repetição quando existirem. Registre entradas e saídas em fronteiras estáveis: requisições, mudanças no banco, registros emitidos, estados, relatórios e erros visíveis. A sequência interna pode mudar com a arquitetura. As obrigações observáveis não podem mudar por acidente.
Um manifesto compacto torna o escopo revisável:
path: close-exception-approval
entry: account-status-change
observations:
- eligible-account-row
- exception-record
- approval-state
- ledger-entry
dynamic-targets:
- reconciliation-program
external-frontier:
- scheduler-calendar
Para cada caso gravado, execute original e substituto com estado controlado, normalize valores que podem variar legitimamente e compare as observações. Um timestamp diferente pode ser aceitável; um registro de exceção ausente não. Quando houver diferença, siga o caminho até a primeira fronteira divergente em vez de comparar milhões de linhas ou só o estado final.
Amostras de tráfego exigem complementos deliberados. Elas refletem a janela de captura, então acrescente casos de limites de calendário, permissões, recuperação, valores raros e ações do operador encontradas pela análise estática. Se ela achar uma ramificação aparentemente alcançável sem amostra, não a descarte em silêncio. Descubra se está inativa, inacessível ou apenas é rara.
CodeHero lê toda a base de código em todas as linguagens e verifica o sistema reescrito com um harness de paridade contra tráfego de produção gravado, entregando o projeto em menos de 30 dias. O padrão útil vale para qualquer abordagem: toda fronteira substituída precisa de evidência no grafo e evidência comportamental que a atravesse.
O grafo é um artefato técnico revisável
Um grafo completo ganha confiança quando engenheiros podem inspecionar evidências, reproduzir a extração e registrar decisões sobre arestas incertas. Um diagrama bonito sem procedência é uma apresentação, não um artefato técnico.
Mantenha o inventário bruto de arestas sob controle de versão, com identificadores estáveis para artefatos e locais. Registre a versão do extrator e a revisão da entrada. Para arestas inferidas, guarde valores e regras. Quando o tráfego confirmar uma aresta, anexe o identificador da amostra em vez de dados sensíveis. Assim, mudanças ficam explicáveis à medida que código e desenho evoluem.
A revisão deve focar limites e incerteza. Pergunte quais resultados visíveis atravessam repositórios, quais alvos dinâmicos seguem abertos, quais dados controlam comportamento posterior e quais sistemas externos injetam trabalho. Um diagrama central gigantesco é uma superfície ruim de revisão. Filtre o mesmo grafo para uma transação, uma fronteira aberta, uma vista de influência entre escrita e leitura ou o mapa dos componentes propostos.
Depois da descoberta, atribua responsabilidade. Entregue arestas abertas a quem possa obter exportações do agendador, definições do banco, configuração ou procedimentos dos operadores. Registre a resposta no grafo, não em notas de reunião. Se um artefato for declarado morto, preserve a evidência, como valores inacessíveis e ausência em tráfego suficientemente representativo. Silêncio não prova desativação.
O primeiro resultado útil não é uma contagem de arquivos nem um total impressionante de nós. É um pequeno conjunto de caminhos completos, com cada transição apoiada em evidência e cada incógnita exposta. Esses caminhos permitem aos arquitetos traçar limites, aos testadores escolher casos e aos operadores reconhecer etapas ausentes. Expanda até que os comportamentos no escopo recebam esse tratamento.
Regras de build e implantação escolhem a aresta real
Nomes no fonte não dizem qual implementação executa. Scripts de build, mapas do linker, ordens de busca, manifestos, descritores de implantação e substituições por ambiente decidem qual candidato recebe o controle. Um grafo que ignora esses artefatos pode ligar o nome certo ao código errado.
Nomes duplicados são normais em sistemas antigos. Uma versão de teste pode estar ao lado da produção. Duas bibliotecas podem conter programas com o mesmo nome de membro. Um projeto VB6 pode referenciar uma interface COM compatível enquanto a implantação escolhe uma implementação registrada. Sinônimos de banco podem levar o mesmo SQL a esquemas diferentes. O grafo precisa de contexto de implantação, não de uma resposta universal que finge executar todos os candidatos.
Modele a resolução como sequência de evidências. Recolha nome e convenção da chamada, aplique a ordem de busca do ambiente, verifique o ponto de entrada e sua forma compatível e anexe o registro que fez a seleção. Se faltar uma entrada, mantenha os candidatos e exponha a decisão ausente em vez de escolher o primeiro arquivo indexado.
Saídas do linker e compilador resolvem perguntas que a análise de fonte não consegue. Um mapa de link mostra o símbolo ligado ao binário. Logs de build mostram versões de fontes gerados e copybooks. Inventários ligam um artefato ao host onde o tráfego o alcançou. Não são documentação secundária, mas evidências sobre o sistema executável, e merecem nós ou anexos estáveis.
Mantenha visíveis as diferenças entre desenvolvimento, teste, recuperação e produção. O mesmo nome lógico pode resolver de forma distinta. Juntar variantes pode esconder uma dependência exclusiva de produção ou levar testadores a crer que executaram código nunca carregado. Marque arestas com o contexto e compare os contextos. Uma diferença pode ser intencional, mas pertence ao escopo se a reescrita deve atender aquele ambiente.
Esse trabalho também encontra declarações obsoletas. Um arquivo de build pode nomear uma biblioteca que não é mais entregue, enquanto o binário resolve tudo em outro lugar. Inversamente, um fonte pode parecer sem uso embora um operador o compile por procedimento separado antes de uma execução anual. Não escolha pela preferência. Registre as evidências conflitantes e obtenha o procedimento ausente.
O teste de uma aresta é simples: outro engenheiro deve seguir suas evidências e chegar aos mesmos candidatos sob as mesmas hipóteses. Reprodutibilidade vale mais que forçar uma resposta única. Dois alvos defensáveis e um arquivo de configuração ausente são melhores que uma seta nítida baseada na proximidade dos diretórios.
A identidade da versão também importa depois do primeiro grafo. Uma dependência ligada a um caminho pode desviar quando muda um branch de entrega, uma biblioteca copiada ou um membro gerado sem mudança do caminho. Guarde um resumo do conteúdo ou identidade de build junto ao nó e ligue o executável implantado àquela revisão exata. Caso contrário, um revisor pode seguir evidências válidas até a versão errada e aprovar a substituição de um comportamento que nunca executou. Isso pesa mais quando equipes entregam arquivos reunidos de várias máquinas, não um checkout limpo. A análise deve informar caminhos duplicados, timestamps incoerentes e artefatos sem vínculo comprovado com o fonte. Os alertas não bloqueiam a descoberta, mas evitam precisão injustificada. Se uma exportação posterior trouxer o binário ou log ausente, a identidade estável permite atualizar as arestas afetadas.
A análise por repositório ainda ajuda uma equipe a entender código local. Ela não estabelece o limite comportamental de um sistema montado com linguagens, jobs, bancos, artefatos gerados e ações humanas. Se as chamadas importantes cruzam os contêineres da análise, esses contêineres já decidiram o que o grafo perderá.
Perguntas frequentes
O que é um grafo de dependências do código completo?
Ele reúne fontes, configuração, jobs, esquemas, artefatos gerados e caminhos observados no escopo. Suas arestas preservam mecanismo e evidência para distinguir chamada direta, passagem de dados e alvo dinâmico derivado.
Por que a análise por repositório perde chamadas?
Repositórios refletem propriedade e entrega, enquanto a execução os atravessa por agendadores, bancos, arquivos, filas e nomes dinâmicos. Sem ver as duas pontas, o analisador mantém um nome aberto ou perde a aresta inteira.
A análise estática resolve chamadas dinâmicas?
Muitas podem ser limitadas a candidatos seguindo atribuições, parâmetros, configuração, campos e regras da plataforma. O resultado deve ser marcado como evidência derivada, não como chamada literal confirmada.
Leituras e gravações no banco pertencem ao grafo?
Sim, quando transportam comportamento entre componentes. Registre operação, campos relevantes e predicado do leitor, pois tocar a mesma tabela não implica influência.
Ler toda a árvore encontra cada dependência?
Não. Agendadores externos, configuração, procedimentos, triggers e binários podem ficar fora, então o grafo precisa de uma fronteira aberta. Observações e exportações do ambiente fecham algumas lacunas.
Como rastros de execução melhoram o grafo?
Confirmam que caminhos e valores dinâmicos apareceram no tráfego gravado. Não provam que os caminhos ausentes estejam mortos, portanto combine-os com alcançabilidade estática e casos raros.
Como representar código gerado?
Mantenha gerador, modelos, entradas e saídas como nós separados, ligados por arestas de geração. Assim ninguém edita uma saída que será sobrescrita, e os nomes concretos da implantação permanecem.
Um grafo pode definir novos limites de serviços?
Ele fornece evidências sobre comportamento coeso, regras transacionais, influência dos dados e interfaces reais. Não transforme cada programa ou repositório antigo em serviço só porque o mapeamento é conveniente.
O que um harness de paridade deve comparar?
Compare efeitos observáveis de caminhos completos: respostas, mudanças no banco, registros emitidos, estados, relatórios e erros. Normalize apenas valores que podem diferir e investigue a primeira fronteira divergente.
Como revisar um grafo com milhões de linhas?
Revise caminhos filtrados e vistas de incerteza, não um diagrama enorme. Arestas estáveis com mecanismo, localização e evidência permitem tratar uma transação, conjunto de alvos ou fronteira por vez.