Elton José logo
Elton José
Análise de Impacto

Análise de Impacto por Grafo: 70% Menos Regressão em Código Gerado por IA

Análise de Impacto por Grafo: 70% Menos Regressão em Código Gerado por IA
0 visualizações
18 minutos de leitura
#Análise de Impacto

Análise de Impacto por Grafo: 70% Menos Regressão em Código Gerado por IA

Os pull requests que mais me custam tempo não são os grandes; são os de dezenove linhas. Um deles me tomou quase três horas. O agente tinha ajustado o comportamento de um método de serialização que, olhando o diff, parecia perfeitamente contido: um arquivo, uma função, uma condição a mais. O código estava correto no sentido estrito. A suíte que rodou no pipeline passou. E mesmo assim quebrou dois consumidores que ninguém tinha lembrado de mencionar, porque a dependência entre eles não passava por nenhum import direto.

O que me incomodou não foi o incidente em si. Foi perceber que nenhuma das ferramentas que tenho no pipeline hoje tinha condição de me avisar. O agente leu o diff, o revisor humano leu o diff, o CI rodou os testes que estavam próximos dos arquivos alterados, e todos os três estavam olhando exatamente o mesmo recorte: o texto que mudou. Ninguém olhou o que aquele texto sustentava do outro lado do repositório.

Essa distinção, entre o que mudou e o que depende do que mudou, virou objeto de estudo empírico neste ano. Um trabalho de 2026 chamado TDAD, de Pepe Alonso, mediu quantos testes que passavam antes de uma mudança do agente deixaram de passar depois, e testou o efeito de dar ao agente um grafo de dependências entre código e teste antes que ele escrevesse a correção. A taxa de regressão em nível de teste caiu de 6,08% para 1,82%, uma redução de 70%, com 562 falhas de testes que antes passavam despencando para 155.

É esse número que quero destrinchar hoje, porque ele é menos sobre a ferramenta e mais sobre um princípio de arquitetura de pipeline: agente sem consciência estrutural do repositório não tem como avaliar risco de mudança, e nenhuma quantidade de instrução no prompt resolve isso. Vou passar pelo que o estudo mediu, pela diferença entre contexto sintático e semântico, por como montar essa análise no pipeline, e pelo custo real de fazer isso funcionar num codebase que não é um projeto Python bem-comportado do SWE-bench.


O Número Que Vale A Pena Olhar Com Calma

O experimento rodou sobre o SWE-bench Verified, o subconjunto de 500 issues do GitHub validadas por humanos, usando as primeiras 100 instâncias e o modelo Qwen3-Coder 30B quantizado em 4 bits, rodando localmente. Três configurações foram comparadas: prompt padrão sem nada, prompt com instruções de TDD, e prompt curto acompanhado de um mapa de testes derivado do grafo de dependências.

O detalhe metodológico que faz esse estudo valer é ter medido uma coisa que os benchmarks de agente ignoram. A régua de mercado é a taxa de resolução: a fração de issues em que o patch faz passar os testes que antes falhavam. Ninguém publica quantos testes que já passavam o mesmo patch quebrou no caminho, apesar de o harness do SWE-bench já coletar esse dado no conjunto PASS_TO_PASS. Na baseline, o agente vanilla quebrou 562 testes em 100 instâncias, média de 6,5 por patch gerado.

A severidade também variava de um jeito que a média esconde. Na instância astropy-13977, que tem 322 testes no conjunto de regressão, o agente sem grafo quebrou os 322. Com o grafo, quebrou 12. Regressões catastróficas, definidas como a suíte inteira de testes que antes passavam falhando de uma vez, caíram de três para uma. Esse tipo de caso é o que transforma uma sexta-feira normal numa madrugada de rollback, e é justamente o que uma métrica agregada de "taxa de resolução" trata como ruído.

Preciso ser explícito sobre os limites, porque eles são grandes. São 100 instâncias sem teste de significância formal, com dois modelos locais menores, apenas em Python, sobre repositórios open source excepcionalmente bem testados. O próprio autor lista isso como ameaça à validade externa e reconhece que modelos de fronteira podem se comportar de outro jeito. Eu trataria o "70%" como direção, não como promessa de número que você vai reproduzir no seu monorepo.

O estudo também mostra algo que merece nota: a taxa de resolução caiu de 31% para 29% com o grafo. O motivo é interessante. O agente produziu mais patches vazios, 26% contra 14%, porque quando o mapa indicava risco alto ele se abstinha. Ele "sabia o que não sabia". Para quem opera pipeline de qualidade, é uma troca que eu aceito de olhos fechados.


Diff É Contexto Sintático, Grafo É Contexto Semântico

O motivo pelo qual "leia o diff" não basta em codebase grande é mais estrutural do que parece. Um diff é uma representação puramente sintática: ele descreve, em nível de texto, quais linhas de quais arquivos mudaram. Ele não carrega nenhuma informação sobre relações. Se você mudar a assinatura de uma função que é chamada por trinta lugares, o diff mostra a função e mais nada. As trinta chamadas continuam invisíveis, porque elas não mudaram.

O que o agente precisa é a informação recíproca: quem chama isso, quem importa o arquivo onde isso vive, qual classe herda dessa base, quais testes exercitam esse caminho de código. Isso é contexto semântico, e ele não está no diff nem pode ser inferido dele. Está no repositório inteiro, e só aparece se alguém construir a estrutura que representa essas relações. É a mesma questão que discuti em repository intelligence, quando falei sobre IA que entende a codebase em vez de só ler arquivos soltos: a diferença entre um modelo que tem os arquivos e um modelo que tem o mapa.

Sem esse mapa, o agente tem duas opções e as duas são ruins. A primeira é rodar a suíte inteira do repositório antes de cada mudança, o que não escala em codebase onde os testes levam vinte minutos ou duas horas. A segunda é rodar só os testes que estão perto dos arquivos alterados, o que funciona bem para acoplamento direto e falha exatamente nos casos que doem: dependência transitiva, onde um módulo três saltos adiante quebra porque uma interface compartilhada mudou de contrato.

O grafo resolve isso porque transforma a pergunta "o que mudou" na pergunta "o que essa mudança alcança". O TDAD constrói esse grafo a partir da AST de cada arquivo Python, com quatro tipos de nó, arquivo, função, classe e teste, e cinco tipos de aresta: CONTAINS para contenção estrutural, CALLS para chamadas resolvidas estaticamente, IMPORTS para rastreamento de import, TESTS ligando teste ao código que ele exercita, e INHERITS para hierarquia de classe. É uma modelagem simples, quase entediante, e é justamente por ser simples que ela roda sem banco de grafo e sem servidor.

O que me chama atenção é que essa modelagem é conhecida há décadas. Chianti, ferramenta de análise de impacto em nível de método para Java, saiu em 2004 usando diferenciação de call graph. A novidade não é a técnica, é o consumidor: em vez de otimizar o fluxo do humano depois do commit, a análise passa a informar o agente antes que ele submeta o patch.


O Que O Agente Precisa Enxergar Antes De Tocar No Código

A parte que mais me interessou na implementação é como o impacto é pontuado. Não basta dizer "esses testes têm relação"; é preciso ordenar por confiança, senão você devolve uma lista de duzentos testes e o agente ignora a lista inteira. O TDAD roda quatro estratégias em paralelo e funde as pontuações, cada uma com um peso base no perfil balanceado:

EstratégiaPesoO que captura
Direct0,95Teste que exercita diretamente o código alterado
Coverage0,80Dependência em nível de arquivo
Transitive0,701 a 3 saltos na cadeia de chamadas até a mudança
Imports0,50Arquivo que importa o arquivo alterado

A pontuação final combina o peso da estratégia com um valor de confiança do vínculo, e quando um teste aparece por múltiplos caminhos só a maior pontuação é mantida. Os testes caem em três faixas, alta acima de 0,8, média entre 0,5 e 0,8, baixa abaixo disso, com teto configurável de cinquenta testes. Existem três perfis de peso: conservador, balanceado e agressivo.

Esse teto e esses perfis não são detalhe de implementação, são decisão de produto do seu pipeline. Perfil agressivo pega mais dependência transitiva e custa mais tempo de execução; conservador devolve uma lista curta e confiável e deixa passar o caso raro. Qual dos dois você quer depende de quanto tempo sua suíte leva e de quão caro é um incidente no seu domínio, e desconfio de qualquer ferramenta que finja que existe padrão universal aqui.

A forma de entrega também importa mais do que eu esperava. O grafo não é servido como banco nem como MCP server: ele vira um arquivo de texto estático, test_map.txt, uma linha por mapeamento de fonte para teste, e o agente usa grep nele. É uma escolha deliberadamente pouco sofisticada, e ela existe porque cada token gasto com infraestrutura é um token que não está descrevendo o código.


O Paradoxo Do TDD: Instrução Sem Contexto Piorou Tudo

O achado mais desconfortável do estudo não é o do grafo. É o do meio-termo. A configuração que adicionou instruções de workflow de TDD ao prompt, sem grafo nenhum, teve taxa de regressão de 9,94%, pior que a baseline de 6,08%. Quarenta e dois por cento mais testes quebrados do que simplesmente não fazer nada.

O caso mais brutal é django-13089. Com prompt padrão, o agente quebrou 4 dos 352 testes de regressão. Com instrução de TDD e sem grafo, quebrou os 352. A explicação que o autor oferece tem duas partes, e as duas fazem sentido para mim. A primeira é mecânica: prompt verboso consome tokens de contexto que o modelo de 30B precisava para carregar código real do repositório. A segunda é comportamental: agente instruído a fazer TDD tenta correções mais ambiciosas, toca mais arquivos, e sem saber quais testes estão em risco essa ambição vira dano colateral.

A conclusão prática que o autor extrai disso é a frase que ficou comigo: agentes não precisam que você diga como fazer TDD, precisam que você diga quais testes verificar. Contexto vence procedimento. E tem uma evidência de ablação que reforça isso: encurtar o prompt sem adicionar grafo derrubou a resolução de 30% para 20%, e dobrar o tamanho do prompt sem grafo produziu resolução idêntica. Nem brevidade nem verbosidade explicam o ganho sozinhas. É a combinação de prompt curto com mapa derivado do grafo que funciona.

Não quero que isso seja lido como "TDD com agente não funciona", porque não é o que o dado diz. Ele diz que instrução procedural sem informação estrutural é pior que nada, afirmação bem mais específica e mais útil. Na segunda fase do mesmo experimento, com outro modelo e outro framework, a regressão ficou em zero nas duas configurações e a resolução subiu 8 pontos percentuais com o mapa de testes presente. Vou voltar a esse tema na sexta, quando pretendo falar sobre TDD como mecanismo de controle sobre agentes que apagam testes em vez de consertá-los, que é o outro lado dessa mesma moeda.


Como Montar Isso No Pipeline Sem Virar Projeto De Seis Meses

Traduzindo o achado para o mundo onde a gente trabalha, a arquitetura mínima tem três peças: um indexador que percorre o repositório e constrói o grafo, uma etapa de análise de impacto que recebe a lista de arquivos alterados e devolve os testes prováveis, e um artefato estático que o agente consegue consultar sem chamar API nenhuma.

A indexação é a peça que dá mais trabalho e a que mais tem ferramenta pronta. Se o seu stack é poliglota, tree-sitter é o caminho mais direto para extrair AST de forma uniforme. Se você quer resolução semântica de símbolo entre arquivos e repositórios, o formato SCIP, criado pela Sourcegraph para substituir o LSIF, já tem indexadores para as linguagens principais. A vantagem dele sobre uma AST caseira é resolver referência cruzada de verdade, inclusive entre pacotes, que é onde a análise ingênua de import falha.

Sobre custo computacional, existe um dado útil fora do TDAD. Um trabalho publicado na Empirical Software Engineering em 2025 propôs análise de impacto na granularidade de pull request combinando dependência por call graph com mineração de histórico, calculando um score de risco por PR. Nos experimentos com três projetos open source, o tempo de análise ficou entre 7,4 e 22,43 segundos. Isso é ordem de grandeza de step de CI, não de job noturno. Nos grupos focais, a abordagem recebeu média de 3,66 de 5,0 para melhorar a experiência de revisão, com 71% de concordância sobre as métricas escolhidas, o que é entusiasmo moderado e honesto, não aclamação.

A parte de mineração de histórico daquele trabalho cobre um buraco do grafo puro. Análise estática enxerga dependência declarada no código; ela não enxerga que dois arquivos mudam juntos na maioria dos commits sem ter relação sintática nenhuma. Acoplamento lógico desse tipo é invisível para AST e visível para git log. Se for construir isso, construa as duas fontes de sinal, porque elas erram em direções diferentes.

Na integração com o pipeline, a decisão prática é onde colocar a etapa. Rodar a indexação completa a cada PR é desperdício; o padrão que faz sentido é indexar no merge para a branch principal, publicar o grafo como artefato versionado, e no PR rodar só a análise incremental sobre os arquivos do diff contra o índice mais recente. O resultado vira dois consumidores: o agente, que recebe o mapa antes de escrever o patch, e o revisor humano, que recebe o score de risco anexado ao PR.


O Custo Honesto De Montar Um Grafo

Aqui é onde eu preciso ser chato, porque a parte animadora do post acabou. Análise estática tem limitações estruturais que nenhuma engenharia esperta resolve, e o próprio estudo lista isso: dispatch dinâmico, monkey-patching e código gerado em runtime ficam fora do grafo. Em Python, onde monkey-patching é prática corrente em teste, isso não é caso de borda. Em qualquer linguagem com injeção de dependência pesada, reflexão ou ORM que gera método dinamicamente, uma fatia relevante das arestas reais simplesmente não existe no seu índice.

O segundo custo é o poliglota. Se o seu codebase é TypeScript no front, Go em três serviços, Java no legado e SQL em procedure, você não vai montar um grafo, vai montar quatro, e a fronteira entre eles, que é onde o incidente costuma nascer, fica sem cobertura. Chamada HTTP entre serviços não é aresta de call graph. Contrato de fila, muito menos.

O terceiro custo é manutenção. Índice desatualizado é pior que índice ausente, porque produz falso negativo com aparência de garantia: um mapa não reindexado depois de um refactor grande vai dizer ao agente que nada está em risco, e o agente vai acreditar. Você está adotando não só uma ferramenta, mas um invariante de pipeline, e a falha da reconstrução do índice precisa quebrar o build, não gerar warning que ninguém lê.

O quarto custo é conceitual. Um artigo de visão publicado em 2026 sobre análise de impacto propagada por grafo aponta que as ferramentas atuais se dividem entre as que seguem similaridade semântica e as que seguem dependência estrutural, cada uma com pontos cegos característicos, e que quase todas operam apenas sobre código, ignorando cadeias de requisito e configuração. Traduzindo: mudança que quebra produção via feature flag, variável de ambiente ou migração de schema continua invisível.

Somando tudo: montar análise de impacto por grafo é investimento de plataforma, não de sprint. Faz sentido com codebase grande, suíte lenta, volume alto de PR de agente e um time que consiga ser dono do artefato. Não faz sentido se o seu repositório roda a suíte inteira em quatro minutos, porque aí rodar tudo já é a melhor análise de impacto disponível.


O Que Muda No Code Review Quando O Contexto Vem Antes

Tem um ponto que só ficou claro para mim depois de ler o estudo do TDAD ao lado do estudo sobre agentes de code review. O trabalho de Chowdhury e colegas, apresentado no MSR 2026, analisou 3.109 PRs do dataset AIDev e encontrou que PRs revisados apenas por agentes têm taxa de merge de 45,20%, contra 68,37% dos revisados apenas por humanos, uma diferença de 23,17 pontos percentuais, com abandono muito maior, 34,88% contra 21,60%. E a análise de sinal e ruído mostrou que 60,2% dos PRs fechados revisados só por agente tinham menos de 30% de comentários acionáveis, com 12 dos 13 agentes analisados ficando abaixo de 60% de sinal médio.

Colocando os dois lado a lado, a leitura que faço é que revisão automatizada depois do fato tem retorno decrescente rápido, enquanto contexto estrutural antes do fato tem retorno alto. Faz sentido: o agente revisor recebe o mesmo diff empobrecido que o agente autor recebeu, e sem informação sobre dependência ele produz comentário genérico sobre estilo, que é exatamente o ruído medido ali. Não adianta colocar um segundo modelo para revisar se ele está olhando pelo mesmo buraco de fechadura.

Isso muda a recomendação que eu mesmo escrevi antes. Quando falei sobre code review de código gerado por IA no GitHub, a ênfase estava em como o revisor humano deveria se posicionar diante do volume. Continuo achando aquilo válido, mas hoje eu acrescentaria uma camada antes: parte do que estamos pedindo ao revisor deveria ter sido resolvido por informação estrutural entregue ao autor. Revisor humano é caro demais para ser usado como detector de dependência transitiva.

E converge com o que já tinha visto sobre PRs de agente sendo rejeitados por dev sênior. O estudo de Ehsani e colegas, sobre 33 mil PRs de agentes no GitHub, aponta que PRs não mergeados tendem a envolver mudanças maiores, tocar mais arquivos e falhar no CI do projeto. Mudança grande que toca muitos arquivos é precisamente o cenário em que a distância entre o diff e o impacto real é máxima.

Na prática, o que eu faria primeiro se fosse começar amanhã é bem menos ambicioso do que o post inteiro sugere: pegar os cinco diretórios do repositório onde nasceram os últimos incidentes de produção, indexar só eles, e anexar o mapa de testes impactados ao PR como comentário informativo, sem bloquear nada. Se em dois meses os revisores estiverem usando aquilo para decidir onde olhar, você tem justificativa para expandir. Se ninguém abrir, você economizou o projeto de seis meses.


Conclusão

O que esse conjunto de estudos me deixou não foi entusiasmo com uma ferramenta, foi uma reformulação da pergunta. Eu vinha tratando qualidade de código gerado por IA como problema de revisão, ou seja, como algo que se resolve depois que o patch existe. O dado sugere que uma fatia grande do problema é anterior a isso: o agente não tinha como saber o que estava quebrando, porque ninguém tinha construído a estrutura que responderia essa pergunta.

Também fico com a ideia de que informação vence instrução, que é contraintuitiva para quem passou os últimos anos refinando prompt. O experimento em que simplificar um arquivo de skill de 107 para 20 linhas quadruplicou a taxa de resolução, enquanto adicionar instrução procedural de TDD sem contexto aumentou a regressão em 42%, é um lembrete desconfortável de que a maior parte do que escrevemos em prompt está competindo por espaço com o que o modelo realmente precisava ler.

Não tenho a resposta de quanto disso sobrevive ao contato com um monorepo poliglota de verdade, e o próprio estudo é explícito sobre não ter testado esse cenário. Mas a pergunta que vou levar para a próxima conversa de arquitetura no meu time mudou de forma. Não é mais "qual ferramenta de review vamos plugar", é "o que o agente enxerga do repositório no momento em que decide o que mudar", e essa segunda pergunta é bem mais difícil de responder com um cartão de crédito.

Fontes:

Newsletter

Receba os artigos mais relevantes da semana, sem quebrar seu ritmo de leitura

Um resumo semanal com os melhores posts sobre IA, engenharia de software e tecnologia, enviado no melhor momento para continuar a conversa depois da leitura.

Foto de Elton José

Escrito por

eltonjose

Engenheiro de software e estrategista de produtos digitais, focado em IA pragmática e em transformar experiências de trabalho remoto em aprendizados aplicáveis. Compartilho frameworks e decisões reais que uso em consultorias e projetos.

  • Principais temasAnálise de Impacto, Call Graph
  • Formato do conteúdoGuia prático + insights de carreira