Construir um grafo de dependências COBOL mostrou que a modernização falha na recuperação, não na tradução — e por que uma janela de contexto maior nunca fecha essa lacuna.
COBOLLegacy SystemsSoftware Modernization

Um único arquivo COBOL contou tudo à IA — exceto o fato que importava. Por isso construí o mapa primeiro.

Ashutosh SinghalAshutosh Singhal30 de junho de 202615 min

A linha de COBOL que deu início a tudo tinha três palavras, e cada uma delas mentiu para mim.

COMPUTE WS-RESULT = WS-WIRE-AMOUNT - TRN-LIMIT. Eu estava montando um patrimônio de demonstração para o subsistema de transferência eletrônica de um banco de médio porte, e esta era a linha fatídica dentro de um programa chamado WIRETXN. Parece aritmética que um calouro poderia portar. Subtrair um limite de um valor, gravar o resultado. Se você entregasse esse único arquivo a qualquer modelo moderno e pedisse Java, ele te daria Java limpo, compilável e passando nos testes unitários em cerca de quatro segundos. Ele tiparia TRN-LIMIT como um long. E, na primeira transferência eletrônica ao vivo, gravaria bytes corrompidos em um banco de dados de produção.

Eu sei disso porque TRN-LIMIT não é um long. É um decimal empacotado COMP-3 campo, definido a três arquivos de distância, cuja interpretação ao vivo é escolhida por um flag definido em um programa completamente diferente, sequenciado por um job em lote que roda às duas da manhã. Nada disso é visível em WIRETXN. O arquivo que contém o COMPUTE perigoso não contém nenhum dos fatos que o tornam perigoso.

Essa lacuna é a razão inteira pela qual construí o CodeGraph, e este ensaio é sobre o que errei no caminho até lá. Comecei certo de que o problema era a qualidade da tradução. Eu estava errado. O problema é que o modelo não consegue ver o que precisa ver, e passei um tempo provando a mim mesmo que nenhuma quantidade de "dê mais contexto" resolve isso.

O COMPUTE que parecia seguro e não era

Mapeei a mudança na transferência eletrônica à mão primeiro, antes de confiar em qualquer ferramenta para fazê-lo, e havia exatamente nove fatos que uma migração correta precisava conhecer.

Três deles vivem dentro de WIRETXN e são genuinamente visíveis a um leitor de arquivo único. WIRETXN usa TRN-LIMIT naquele COMPUTE na linha 33. Ele importa um copybook chamado CBACCT, apenas pelo nome, na linha 13. Ele dispara um UPDATE na tabela DB2 ACCOUNTS na linha 37. Uma ferramenta de janela de texto vê os três. Se esses fossem os únicos fatos, o porte ingênuo estaria bem.

Os outros seis são os que doem. TRN-LIMIT é declarado PIC S9(9)V99 COMP-3 em CBACCT.cpy na linha 11, o que significa decimal empacotado, o que significa BigDecimal em Java e absolutamente não long. Logo abaixo, TRN-LIMIT-ALPHA REDEFINES TRN-LIMIT, sobrepondo os mesmos seis bytes sobre o campo como texto bruto. Um terceiro campo, LIMIT-TYPE-FLAG, decide em tempo de execução qual das duas interpretações é a viva. Esse flag é escrito por um programa chamado LIMITSET, e de novo por um job noturno em lote chamado BATCHUPD. E o job JCL NIGHTLY roda às 02:00 como predecessor do job de transferência, que é o único lugar em todo o patrimônio onde a ordenação entre "definir o flag" e "executar a transferência" sequer fica registrada.

Painel de impacto do CodeGraph para TRN-LIMIT mostrando recuperação por grafo com 9 de 9 fatos recuperados contra 3 de 9 da visão ingênua de arquivo único, com cada fato recuperado carregando sua proveniência de arquivo e linha de WIRETXN.cbl e CBACCT.cpy.
O fechamento de TRN-LIMIT no fixture sintético enviado. A recuperação por grafo recupera 9 de 9 fatos de verdade-base, a janela ingênua de arquivo único vê 3 de 9, e cada fato carrega seu próprio arquivo e linha. F4 a F9, os seis que quebram o porte, são os marcados como ocultos em arquivo único.

Seis fatos. Cada um deles verdadeiro, cada um deles indispensável, e cada um deles estruturalmente invisível a partir do arquivo que de fato faz o cálculo. Quando os alinhei assim, o que me inquietou não foi o porte ingênuo estar errado. Foi que o porte ingênuo não tinha como saber que estava errado. Ele leu o único arquivo que lhe foi dado, e o único arquivo silenciosamente omitia os seis fatos que importavam.

O arquivo que contém a linha perigosa não contém nenhum dos fatos que a tornam perigosa. Isso não é um bug de tradução. É uma falha de recuperação vestindo as roupas de um bug de tradução.

Por que parei de tentar aumentar a janela de contexto

Meu primeiro instinto foi o mesmo que todo mundo tem agora, e quero ser honesto: eu o persegui por um tempo — basta dar mais ao modelo.

O raciocínio parecia hermético. Se a falha é que o modelo só viu um arquivo, então alimente-o com o copybook também. Alimente-o com os programas que tocam o flag. Alimente-o com o JCL. As janelas de contexto são enormes agora e ficam maiores a cada trimestre, então certamente a resposta é parar de ser mesquinho e despejar toda a vizinhança de código no prompt. Eu genuinamente esperava que isso funcionasse, e para um exemplo de brinquedo até funciona de certo modo, porque quando você já sabe quais seis arquivos colar, já resolveu o problema de verdade à mão.

Aquela era a rachadura. Para alimentar o modelo com o contexto certo, eu primeiro precisava saber qual contexto era o certo. E saber que TRN-LIMIT cujo tipo é decidido por um flag escrito em BATCHUPD e ordenado por um job JCL às 02:00 não é algo que você extrai lendo WIRETXN com mais atenção. É algo que só se obtém já tendo traçado o grafo de dependências. A janela de contexto não diz o que colocar na janela de contexto. Eu tinha tentado responder a pergunta com a própria resposta.

Então os números tornaram o ponto permanente. Os patrimônios que esses bancos de fato operam não são seis arquivos. São de um a dez milhões de linhas de COBOL, às vezes mais, e 220 bilhões de linhas ainda estão em produção ativa na indústria (meta-análise setorial, 2025). Uma mudança real de transferência eletrônica pode ter um fechamento transitivo de quarenta arquivos ou quatrocentos. Isso nunca cabe numa janela de contexto, nem hoje nem na versão do modelo que chega daqui a três anos, porque o patrimônio cresce mais rápido que a janela e a janela nunca foi a restrição de qualquer forma. A restrição é saber quais quarenta arquivos dos dez milhões são os que essa mudança toca, e provar que você encontrou todos os quarenta e não trinta e oito.

Uma janela de contexto maior é uma resposta melhor a uma pergunta que eu parei de fazer. A pergunta não é "o modelo consegue segurar mais código", é "qual código, e como você prova que é todo ele."

Essa reformulação é a razão inteira pela qual o CodeGraph não é um tradutor. Deliberadamente eu não colo COBOL e devolvo Java. O mapa é o produto, e a tradução é um caso de uso a jusante que qualquer ferramenta pode fazer uma vez que o mapa exista. O que eu construo é a camada de entendimento por baixo. No fixture enviado, o patrimônio se parseia em um grafo de conhecimento tipado de 47 nós e 70 arestas, e o "impacto de uma mudança" é uma travessia de grafo, o fechamento transitivo de tudo o que a mudança toca, com cada aresta carregando o file:line de onde veio. É deliberadamente trabalho de grafo em Python puro e chato, sem modelo no hot path, porque o que eu preciso que ele seja não é esperto. Preciso que seja completo e reproduzível. Mesmo fixture na entrada, mesmo fechamento na saída, todas as vezes.

Fico repetindo isso para mim mesmo como regra. Agentes aconselham, o código decide. A camada de linguagem opcional na demo, a parte que responde perguntas sobre o fechamento em inglês simples, fica desligada por padrão e atrás de uma chave. O valor não depende dela. O valor é a recuperação e a prova, e nenhuma das duas é uma capacidade de modelo.

O que a visão ingênua de fato apaga?

Construí um interruptor na demo especificamente para poder ver os seis fatos desaparecerem, porque eu não acreditava de todo na falha até vê-la acontecer.

Marque "Naive AI context view" e o grafo colapsa para o único arquivo-fonte mais uma janela de linhas em torno da mudança, que é exatamente o que uma ferramenta de janela de texto alimenta a um modelo. O painel que lia 9 de 9 cai para 3 de 9. Os três fatos no arquivo permanecem acesos. Os outros seis ficam cinza e se calam: o COMP-3 tipo, o REDEFINES overlay, o flag controlador, seus dois escritores entre módulos, e o predecessor JCL das 02:00. Um banner vermelho soletra a consequência nas próprias palavras do app: que, munido só dos três fatos visíveis, um modelo emite long TRN_LIMIT e corrompe o banco de dados.

Visualização de contexto de IA ingênua do CodeGraph ativada, esmaecendo seis dos nove fatos para cinza com um banner vermelho afirmando que apenas 3 de 9 fatos vivem dentro de WIRETXN.cbl e o resto é invisível a uma ferramenta de janela de texto.
A visão ingênua de arquivo único, que é uma simulação do que uma ferramenta de janela de texto de fato vê, não um conector ao vivo. Seis fatos esmaecem para cinza. O banner nomeia cada coisa que sumiu: o tipo COMP-3, o overlay REDEFINES, o flag controlador, seus dois escritores, e o predecessor das 02:00.

Quero ter cuidado aqui, porque este é exatamente o ponto em que um fundador é tentado a exagerar. O resultado 9-versus-3 é medido no fixture sintético de transferência eletrônica enviado, um patrimônio que eu escrevi à mão para esta demo, precisamente para que o conjunto verdadeiro de dependências seja conhecido e o número de recall seja uma medição rotulada de verdade, e não uma impressão subjetiva. Não é uma garantia sobre o seu COBOL. A visão ingênua é uma simulação, não um pipeline z/OS ao vivo. O grafo está em memória com SQLite por baixo, não uma plataforma de grafo de produção. Construí um banco sintético porque eu não poderia mostrar eticamente um real, e porque uma verdade-base conhecida é a única forma honesta de dizer "o grafo pegou todos os nove e o arquivo único pegou três."

Mas a forma da falha não é sintética, e essa é a parte que importa. O COMP-3 campo cujo tipo é decidido em outro lugar, o flag definido por um job em lote, a ordenação que só existe no JCL — essa é a textura ordinária de um patrimônio bancário de quarenta anos, não casos-limite exóticos. Quando cerca de 70 a 80 por cento dos projetos de modernização de mainframe falham em atingir seus objetivos (meta-análise setorial, 2025), eu não acho mais que seja porque a etapa de tradução é ruim. A etapa de tradução está bem. Ela recebe uma imagem com os seis fatos mais importantes recortados.

Prova, ou não conta

A funcionalidade de que mais me orgulho é a que admite o que não consegue fazer, e eu não apreciava isso até uma conversa de compliance reenquadrá-la para mim.

Um engenheiro quer uma migração correta. Um regulador quer algo diferente e mais difícil: evidência. Sob o DORA, um banco deve manter um inventário de ativos de TIC. Sob o SOC-2, deve manter recibos de controle de mudanças. Nenhum dos dois é satisfeito por um modelo dizendo "confie em mim, encontrei as dependências." Eles precisam de uma prova de completude, uma declaração de quanto do código-fonte a ferramenta de fato conseguiu resolver e, mais importante, um flag honesto sobre o que não conseguiu. Então construí um gate de completude. Toda referência PERFORM, CALL, COPY, e DB2 no fixture tem de resolver para um nó real no grafo ou ser marcada como "precisa de revisão." Nada pode sumir em silêncio.

No fixture, esse gate resolve 33 de 34 referências, o que é 97,1 por cento de cobertura. A que não consegue resolver é um programa chamado DISPATCH, que faz um dinâmico CALL WS-PROGNAME, um alvo computado em tempo de execução que nenhum parser estático consegue seguir porque o destino não é conhecido até o programa rodar. E o comportamento certo aí não é adivinhar. É levantar um flag que diz "um humano precisa olhar este", e deixá-lo no relatório.

Aba de auditoria do CodeGraph mostrando 97,1 por cento das referências resolvidas com 1 marcada para revisão, o item marcado sendo o CALL WS-PROGNAME dinâmico de DISPATCH em DISPATCH.cbl linha 15, listado como marcado e não descartado em silêncio.
O gate de completude no fixture. 33 de 34 referências resolvem, 97,1 por cento, e a única não resolvida, o CALL dinâmico de DISPATCH para um alvo computado em tempo de execução, é marcada para revisão em vez de descartada. A honestidade sobre a que não consegue seguir é o ponto, não uma nota de rodapé.

Aquele DISPATCH call marcado é minha coisa favorita em toda a construção, e eu quero dizer isso. Uma ferramenta que resolve 97 por cento e te diz exatamente quais 3 por cento não conseguiu vale mais do que uma ferramenta que afirma 100 e esconde a lacuna, porque a lacuna escondida é onde vive a transferência eletrônica corrompida. O gate de completude produz um "Relatório de Topologia e Completude do Código-Fonte" exportável, um JSON e um HTML imprimível com o resumo de nós e arestas, os fechamentos por módulo com proveniência file:line proveniência, o resultado de recall, e os itens marcados com um carimbo de tempo. Esse artefato é o ponto. É a coisa que você pode entregar a um regulador, rerodar no trimestre seguinte, e obter a resposta idêntica porque é determinístico.

Eu prefiro enviar um número que admite o próprio furo a um mais redondo que o esconde. O CALL dinâmico marcado não é uma fraqueza na demo. É a demo.

Esta também é a parte que não envelhece. Um modelo perfeito, um que nunca alucina uma única linha de Java, ainda assim não consegue provar a um regulador quais dependências recuperou. Ainda assim não consegue seguir um CALL computado em tempo de execução estaticamente, porque isso é uma propriedade do código e não do leitor. Proveniência e completude são propriedades do sistema que você constrói em torno do modelo, não capacidades que se desbloqueiam escalando-o.

A ordem em que você toca as coisas

A última coisa que o grafo me deu foi algo que eu nem tinha me proposto a construir: uma ordem segura para fazer o trabalho.

Uma vez que você tem a topologia completa de dependências, pode pontuar cada programa pelo quanto está entrelaçado. Eu uso uma fórmula simples, acoplamento ponderado contra COMP-3 armadilhas, criticidade JCL e calls não resolvidos, e ela ranqueia os quatorze programas do fixture numa ordem de extração strangler-fig. O programa de menor risco extrai primeiro, o programa-deus por último. No fixture, AUDITLOG sai no posto 1 com score de risco zero, porque não tem acoplamento e nada depende de ele estar certo. É o lugar seguro para começar. O programa WIRETXN com o qual temos nos preocupado fica no posto 11, carregando sua armadilha COMP-3 e sua criticidade JCL. DISPATCH, com seu call dinâmico não resolvido, fica no posto 12. E ACCTMGR, o programa-deus em que tudo se apoia, extrai por último no posto 14 com score de risco 15.

Aba de extração do CodeGraph mostrando os quatorze programas do fixture ranqueados em ordem strangler-fig, AUDITLOG no posto 1 com score de risco 0 e ACCTMGR no posto 14 com score de risco 15, colunas para acoplamento, armadilhas COMP-3 e criticidade JCL.
A ordem de extração strangler-fig no fixture. AUDITLOG extrai primeiro com risco 0, o programa-deus ACCTMGR extrai por último com risco 15, e WIRETXN e DISPATCH ficam altos na lista por sua armadilha COMP-3 e call dinâmico não resolvido. Sequência é uma propriedade do grafo, não um julgamento.

Eu não esperava me importar tanto com sequenciamento quanto me importo agora. Mas é a mesma lição pela terceira vez. Onde você pode começar com segurança é um fato sobre a topologia, não uma opinião que se discute numa reunião de planejamento. Uma equipe olhando para um milhão de linhas não discorda de verdade sobre como traduzir um parágrafo. Ela discorda, sem fim e caro, sobre onde começar e o que quebra se tocarem a coisa errada primeiro. Essa é uma pergunta de grafo, e o grafo a responde do mesmo jeito a cada execução.

A ordem de extração, o gate de completude, o fechamento de impacto — todos são o mesmo objeto visto de três ângulos. Recupere a fatia verdadeira, prove que é a fatia inteira, e ranqueie as fatias por risco. Nenhum desses três é um problema de tradução, e nenhum deles é resolvido por um modelo mais inteligente.

A pergunta a que continuo voltando

Comecei a fazer uma pergunta a cada pitch de modernização com IA que vejo, inclusive o meu, e ela se tornou em silêncio a única em que confio.

Não "ele escreve Java bom", porque a resposta é quase sempre sim e quase nunca importa. A pergunta mais difícil é a que a linha TRN-LIMIT me ensinou: ele consegue provar, agora, quais dependências recuperou, e essa prova sobreviveria a um regulador que quisesse fazê-la falhar. Se a ferramenta não consegue me mostrar o fechamento com proveniência file:line e não consegue me dizer honestamente o que não conseguiu resolver, então não importa quão fluente a saída pareça. É adivinhação com boa gramática, e eu vi exatamente esse chute tipar long sobre um campo decimal empacotado e ir atrás do banco de dados.

A indústria passou uma década melhorando a etapa de tradução enquanto 70 a 80 por cento dos projetos continuavam a errar os objetivos (meta-análise setorial, 2025), e acho que é porque a etapa de tradução nunca foi onde o risco vivia. O risco vive na topologia, nos seis fatos invisíveis, no flag definido às duas da manhã. Se você quiser ver um grafo recuperar esses seis fatos e depois marcar o que honestamente não consegue, a demo está aqui: veriprajna.com/pt-BR/demos/modernizacao-cobol-com-um-grafo-de-conhecimento.

E se você preferir ver a ler eu descrevendo, aqui está a coisa inteira rodando de ponta a ponta.

Eu não acredito mais que o próximo lançamento de modelo é o que desbloqueia essas migrações. Uma janela maior segura mais código; ela não sabe qual código, e não consegue provar que encontrou todo ele. Isso era verdade quando digitei a primeira linha do parser, e acho que ainda será verdade muito depois de o modelo que usei para construir isto ter sido aposentado. O mapa sempre foi a parte difícil. Nós só ficamos olhando para a tradução porque era a parte que sabíamos como avaliar.

Pesquisa relacionada

Também publicado em

Construa sua IA com confiança.

Faça parceria com uma equipe que tem profunda experiência na construção da próxima geração de IA empresarial. Deixe-nos ajudá-lo a projetar, construir e implementar uma estratégia de IA em que você possa confiar.

Veriprajna consultoria de Deep Tech é especializada na construção de sistemas de IA críticos para a segurança nas áreas de saúde, finanças e domínios regulatórios. Nossas arquiteturas são validadas em relação a protocolos estabelecidos, com documentação de conformidade abrangente.