- Avaliações de agentes de programação autônomos têm observado principalmente a taxa de sucesso em tarefas dentro de codebases fixas, mas este estudo isola e mede se a limpeza do próprio código altera o custo de exploração e modificação
- Foram criados 6 repositórios de pares mínimos com a mesma arquitetura, dependências e comportamento externo, diferindo apenas em violações de regras do SonarQube e complexidade cognitiva, além de 33 tarefas baseadas em testes ocultos
- Com Claude Code e Claude Sonnet 4.6, cada tarefa foi executada 10 vezes em cada lado dos pares de repositórios, totalizando 660 experimentos; o agente não sabia em qual lado do código estava
- A limpeza do código não alterou a taxa de aprovação, mas, em código mais limpo, as métricas equivalentes a tokens caíram 7–8% e as revisitas a arquivos diminuíram 34%
- Não apenas o modelo ou o prompt, mas também o estado da codebase continua sendo uma variável prática que determina o custo computacional e a eficiência de exploração dos agentes
Pergunta de pesquisa e definição do problema
- Agentes de programação autônomos estão se disseminando rapidamente
- Em uma análise de 128.018 projetos do GitHub em 2026, menos de um ano após o lançamento dos primeiros agentes práticos, foram encontrados indícios de atividade de agentes em 22–29% dos projetos
- O custo de execução de agentes também não é pequeno
- No SWE-bench Verified, uma única tarefa usa em média cerca de 4 milhões de tokens com LLMs de fronteira
- No uso total, os tokens de entrada representam a maior parte
- Avaliações existentes têm se concentrado em se a tarefa foi resolvida em benchmarks como o SWE-bench, e estudos recentes começaram a medir também o uso de recursos junto com a taxa de aprovação
- A comparação típica fixava a codebase e alterava o agente ou o scaffolding
- Este estudo faz o inverso: fixa o agente e as tarefas e compara alterando apenas a limpeza da codebase
Limpeza de código e construção de pares mínimos
- A limpeza de código é tratada como um conjunto de características associadas a código manutenível
- Legibilidade
- Baixa complexidade cognitiva
- Helpers bem separados
- Nomes claros
- Pouco código morto, lógica duplicada e acoplamento acidental
- O estudo não fixa a limpeza do código como uma definição formal rigorosa; usa o número de violações de regras de análise estática do SonarQube como uma proxy flexível
- A ferramenta usada é o SonarQube Cloud Enterprise Edition
- O conjunto de regras é o “default quality gate”
- Os repositórios de pares mínimos foram construídos para diferir apenas na limpeza interna, mantendo as seguintes condições
- Mesma linguagem e framework
- Mesmas dependências
- Mesmos testes ou cobertura de testes equivalente
- Mesmo comportamento externo
- A equivalência comportamental é considerada quando, para a mesma entrada, as saídas e transições de estado observáveis externamente são iguais
- Na prática, a verificação é feita passando a mesma suíte de testes com a mesma cobertura ou, em casos como refatorações, passando pares de testes ajustados para mudanças não observáveis
Pipeline de geração de pares mínimos: Slopify e Vibeclean
- Os pares mínimos são criados em duas direções
- Slopify, que torna um repositório limpo mais bagunçado
- Vibeclean, que limpa um repositório bagunçado
-
Slopify
- Transforma uma codebase limpa em uma versão que parece ter crescido sem code review ou linting
- O objetivo não é criar código propositalmente quebrado, mas uma versão de história alternativa de como ele poderia ser sem análise estática
- Três etapas são executadas, cada uma por um novo agente
- Build: compila o repositório, garante que os testes passem e fixa os comandos em
build instructions.md - Explore: percorre o repositório e escreve um
summary.mdpara cada diretório alvo de limpeza - Transform: introduz violações de regras do SonarQube nos diretórios designados e, após cada passagem, executa os testes novamente para rejeitar mudanças que quebrem o comportamento
- Build: compila o repositório, garante que os testes passem e fixa os comandos em
- Aumenta a complexidade cognitiva por meio de inline de helpers, duplicação de lógica por caminho, adição de código morto e fusão de alguns módulos em arquivos únicos
-
Vibeclean
- Limpa uma codebase que naturalmente tem muitas violações de regras, mantendo o comportamento externo
- A lista de tarefas do agente é a lista de issues detectadas pelo analisador, e cada issue é vinculada a um intervalo de código
- O escopo das correções é limitado aos problemas indicados pelo analisador; uma reformulação completa não é o objetivo
- Opera em duas etapas
- Build: verifica os comandos de build e teste e os fixa em
build instructions.md - Clean: limpa mecanicamente, módulo por módulo, as violações de regras do analisador e confirma a equivalência comportamental com testes após processar cada módulo
- Build: verifica os comandos de build e teste e os fixa em
- Remove duplicação de literais de string, apaga código comentado, substitui idioms de coleções legadas e remove ramificações mortas
- Quando o analisador aponta estruturas realmente grandes, transforma um
dispatch switchde mais de 200 linhas em helpers nomeados ou extrai helpers de persistência de uma classe de 2.800 linhas - No entanto, a extração pode redistribuir a complexidade para mais métodos em vez de eliminá-la, e algumas das maiores estruturas gigantes permanecem como
wontfix
Repositórios de benchmark e desenho das tarefas
- O benchmark é construído sobre o Harbor framework v0.4.0
- No total, são usados 6 repositórios de pares mínimos
- 3 centrados em Java e 3 centrados em Python
- Alguns repositórios incluem pequenas quantidades de código em outras linguagens
- São compostos por 3 repositórios open source públicos e 3 codebases privadas da SonarSource
- Os pares privados servem como prevenção contra memorização, considerando a possibilidade de os LLMs avaliados terem treinado em repositórios públicos
- Os principais números do lado limpo e do lado bagunçado de cada repositório são os seguintes
sonar-sca*: 94 / 2.825 issues, densidade de issues 0,73 / 20,66, densidade de complexidade cognitiva 30,6 / 56,5sonar-caas-poc*: 16 / 855 issues, densidade de issues 0,61 / 27,16, densidade de complexidade cognitiva 179,8 / 218,9sonarcloud-codedatalake*: 199 / 1.319 issues, densidade de issues 4,36 / 34,39, densidade de complexidade cognitiva 34,0 / 216,5commons-bcel: 694 / 2.711 issues, densidade de issues 12,60 / 49,46, densidade de complexidade cognitiva 102,8 / 108,3genie: 152 / 1.262 issues, densidade de issues 1,28 / 10,81, densidade de complexidade cognitiva 22,2 / 23,5ckan: 1.006 / 3.632 issues, densidade de issues 7,54 / 27,50, densidade de complexidade cognitiva 69,3 / 76,5
- O desenho das tarefas segue três regras
- Passagem por hotspots: posicionar tarefas de modo que atravessem regiões de código onde a diferença de densidade de issues e complexidade cognitiva é grande entre os dois lados do par
- Descrição observável externamente: fornecer apenas entradas, saídas e cenários de exemplo, sem nomes de arquivos, funções ou estruturas internas
- Testes pela superfície pública: executar testes ocultos por meio das interfaces que a aplicação oferece ao chamador, como CLI, rotas HTTP, bibliotecas/APIs
- A geração das tarefas é dividida entre agentes e humanos
- Um agente compara a variante limpa e a variante bagunçada e cria um mapa de diferenças
- Outro agente escreve o resumo da tarefa e a viabilidade de teste
- Um humano seleciona, edita e faz curadoria dos resumos plausíveis e interessantes
- Um terceiro agente cria as instruções reais, os testes ocultos pela superfície pública e a implementação de referência interna
- A implementação de referência precisa passar nos testes ocultos nos dois repositórios
- O repositório antes da modificação não deve passar nos testes ocultos
- Tarefas que ainda não satisfazem as condições após duas iterações são reescritas por humanos ou removidas
- O número final de tarefas é 33, divididas em três trilhas
- 13 tarefas de hotspot cognitivo: atravessam áreas de alta complexidade em um único método ou uma única classe
- 14 tarefas multimódulo: exigem mudanças que atravessam dois ou mais módulos
- 6 tarefas de controle: executam tarefas simples nas mesmas áreas dos dois lados para verificar se há mudanças não relacionadas à limpeza
Configuração experimental e métricas
- Todos os experimentos são executados com Claude Code, usando o conjunto padrão de ferramentas
- Os números reportados vêm de execuções com Claude Sonnet 4.6
- O Claude Haiku 4.5 também foi testado superficialmente no mesmo conjunto de tarefas, mas foi excluído dos resultados principais porque a taxa de aprovação era baixa demais para interpretar claramente diferenças de footprint
- O agente lê apenas a descrição da tarefa
- Não recebe priming adicional sobre limpeza de código
- Não sabe em qual lado do par mínimo está trabalhando
- Cada tarefa é executada 10 vezes em cada lado do par
- O número total de experimentos é 33 × 2 × 10 = 660
- Cada execução ocorre em um sandbox conteinerizado
- CPU, memória, armazenamento e tempo de relógio são limitados
- Há acesso a registros públicos de pacotes
- A imagem base inclui toolchain, cache de build e serviços específicos de cada repositório
- Dentro de um par, apenas a árvore de código-fonte montada em
/appdifere
- São registradas 10 métricas
- Taxa de aprovação: proporção em que os testes ocultos passam no estado final
- Tokens de entrada: número de tokens lidos pelo modelo em todos os turnos, compostos majoritariamente por conteúdo de arquivos e reenvio do histórico da conversa
- Tokens de saída: toda a produção do modelo e de subagentes, incluindo prosa, código, rastros de raciocínio e chamadas de ferramentas
- Número de caracteres de raciocínio: como a API da Anthropic não expõe tokens de raciocínio separadamente, contam-se os caracteres em texto puro dos blocos de conteúdo de raciocínio
- Número de turnos de conversa: total de trocas agente-ferramenta
- Número de turnos antes da primeira edição: turnos até a primeira modificação de arquivo
- Número de caracteres antes da primeira edição: caracteres da conversa no mesmo intervalo
- Número de arquivos lidos: quantidade de arquivos únicos abertos durante a execução
- Revisitas a arquivos: número de vezes em que um arquivo já lido e modificado é lido novamente
- Linhas modificadas: linhas de código alteradas pelo patch final
- Revisitas a arquivos aparecem em fluxos como
ler → editar → possível outro trabalho → ler novamente- No estudo, isso é interpretado como sinal de incerteza sobre uma edição anterior, mais do que exploração ampla
- Como métricas de footprint do agente podem variar bastante mesmo ao repetir a mesma tarefa com temperatura fixa, é aplicado um filtro de outliers
- Dentro de cada combinação
(tarefa, lado), execuções que se desviam mais de 50% da mediana das 10 repetições são removidas antes da média - Na prática, 9,7% de todas as execuções são removidas
- Dentro de cada combinação
- Os números no nível do dataset são micro-médias das 33 tarefas
- Para cada métrica, somam-se as médias do lado limpo e do lado bagunçado por tarefa, e então calcula-se a diferença proporcional
- A taxa de aprovação, como exceção, é reportada como diferença absoluta em pontos percentuais entre o lado limpo e o lado bagunçado
Resultado: impacto maior no custo de exploração do que na taxa de sucesso
- A limpeza do código não produz mudança significativa na taxa de aprovação do agente
- Em código mais limpo, as métricas equivalentes a tokens diminuem 7–8%
- As revisitas a arquivos caem 34%, mostrando que código limpo pode reduzir o custo de reler e refazer o caminho mesmo na mesma tarefa
- Portanto, a limpeza do código pode ser vista como um eixo separado que influencia o custo de execução de agentes, junto com a escolha do modelo, o harness e o prompt
1 comentários
Opiniões do Hacker News
É uma pergunta interessante para pesquisar, mas sou muito cético quanto ao desenho experimental
No experimento, eles sintetizaram bases de código “degradadas” ou “limpas” com o Opus 4.6 e as usaram para comparação relativa
Pior ainda é que não controlaram se os testes da aplicação foram quebrados
“A taxa de aprovação avalia o estado final do agente com base em testes ocultos que escrevemos para cada tarefa. Não verificamos se o agente quebrou testes não relacionados que já existiam no repositório e, mesmo que as soluções do lado limpo e do lado bagunçado passem nos testes ocultos, elas podem diferir em testes não avaliados”
Se a qualidade do artefato final não é controlada, as conclusões sobre consumo de tokens parecem quase sem sentido
Para uma comparação justa, seria preciso escrever um único conjunto de testes que funcione nos dois projetos de cada par
Não estou dizendo que o estudo seja bom, mas dá para entender essa decisão, já que passar em testes não necessariamente se correlaciona com a eficácia do agente
Ela só mostra uma situação em que o código parece funcionalmente ok, mas custa mais tokens para concluir a tarefa
O cansaço com IA ficou entediante há muito tempo; agora é simplesmente doloroso
Pela minha experiência, há uma diferença considerável no desempenho dos agentes entre uma base de código cheia de código morto, código duplicado, caminhos alternativos inalcançáveis, abstrações vazando e padrões de projeto imaturos, e outra em que o fluxo de dados é claro e o encapsulamento e a estrutura são limpos
Em código ruim, todos os modelos de ponta precisaram passar por várias rodadas de revisão de código, verificação de qualidade e correções; em código bom, vi acertarem já na primeira ou segunda tentativa
Você pode fazer o LLM executar scripts que verificam esses itens e também impor os mesmos scripts como hooks de pre-commit
Colocar esse tipo de configuração de forma rigorosa em todas as bases de código em que trabalho foi o que teve o maior efeito na codificação com agentes
Escrevi mais detalhadamente sobre os vários linters que uso aqui: https://www.balajeerc.info/Use-Deterministic-Guardrails-for-...
Acho inevitável que agentes se saiam melhor em uma base de código limpa do que em uma enorme bagunça
É como quando eles se saem melhor tendo acesso a especificações e documentação bem feitas
Em algum momento, aqueles padrões horríveis começam a grudar em mim também
Aqui há um artigo tentando responder à pergunta, e testemunhos anedóticos apenas enviesam o leitor, sem acrescentar valor para uma conclusão objetiva sobre o problema
A discussão mais útil seria todos lerem o artigo e criticarem a metodologia ou os resultados
Ainda assim, as pessoas que reclamam que LLMs não são tão bons geralmente parecem ser do tipo que tem bases de código bagunçadas
Um truque que vi funcionar bem, em Python, é mandar fazer refatoração assim
“Refatore o código Python para ficar mais pythônico. Por exemplo, reduza classes e singletons, especialmente se isso trouxer ganhos de velocidade. O código Python deve obrigatoriamente seguir os padrões de organização de código esperados em pacotes Python open source populares, sem regressão de desempenho nos benchmarks”
Para código Rust, usei uma variação assim
“A base de código Rust em
/srcinchou e agora tem vários arquivos com mais de 1.000 linhas. Refatore a base de código Rust para atender aos padrões de organização de código esperados em código Rust open source popular, sem regressão de desempenho nos benchmarks”Esses prompts parecem melhorar o desempenho do agente porque a) reorganizam o código de forma lógica e b) os nomes dos arquivos dão pistas semânticas sobre onde fica o código relevante
Em um arquivo inchado de 5 mil linhas, o agente precisa ler vários blocos para encontrar o código relevante, o que é ineficiente
O desempenho dos benchmarks também costuma melhorar depois da refatoração, especialmente em Rust compilado; pode ser coincidência, mas não é algo de que eu vá reclamar
Em geral, ferramentas de codificação com agentes tendem a relutar em apagar código. Mesmo quando você manda apagar, elas fazem de tudo para manter o código antigo ou acrescentar complexidade para que ele ainda possa ser chamado
Se você está apenas prototipando, isso é muito irritante, porque acaba acumulando bastante código morto e depois gera confusão quando você tenta adicionar funcionalidades
Depois que você percebe isso, basta pedir para remover o legado
Manter a base de código limpa incentiva a IA a fazer a coisa certa. Se há muitos testes, ela adiciona mais ao criar novas funcionalidades; se há documentação, ela a atualiza sem você precisar pedir
À medida que os code harnesses melhorarem, essas coisas vão ser cada vez mais incorporadas, e ficará mais fácil para pessoas com pouca experiência em prompts obterem bons resultados
Normalmente peço primeiro uma revisão para criar uma lista de itens de revisão e depois passo por cada item junto com ela, decidindo sim/não ou sugerindo ajustes adicionais
Especialmente se não houver testes ponta a ponta cuidadosos para o sistema inteiro
De todo modo, as regras de estilo de código já estão no CLAUDE.md
A abordagem de “um pipeline de agente que limpa um repositório bagunçado” parece terrível e, por si só, suficiente para invalidar o estudo inteiro
Parece que metade dos pares mínimos deste trabalho foi criada desse jeito
Eu não confiaria em nenhuma conclusão que exija assumir que um repositório “limpo” por IA realmente representa uma boa base de código
Aqui, “limpo” não significa que pedimos ao agente simplesmente para escrever código melhor
Fornecemos uma lista de 50 a 100 violações de regras de analisadores estáticos e a quantidade de linhas de código, e pedimos que elas fossem removidas
Depois verificamos se as violações das regras tinham sido resolvidas
Reescrever código com LLMs para remover esse tipo de violação é uma prática bastante aceita
A abordagem one-shot existente da Sonar baseada em LLM [1] está em operação há mais de um ano, e a abordagem agentic recente [2] também executa essa mesma tarefa muito bem
[1] https://www.sonarsource.com/solutions/ai/ai-codefix/
[2] https://www.sonarsource.com/products/sonarqube/remediation-a...
Acho óbvio que isso só pode influenciar
Nenhum modelo consegue colocar uma base de código real inteira no contexto, então precisa percorrer o código como uma pessoa faria
Ele pesquisa e lê arquivos
Se o arquivo estiver onde se espera e tiver um nome que o modelo ou uma pessoa provavelmente procuraria primeiro, ele encontra na primeira tentativa; caso contrário, precisa de buscas mais profundas e várias tentativas
Basta ver todos os caminhos e ir passando para o próximo, ignorando as áreas já tratadas
É bem parecido com a forma como um desenvolvedor trabalha
Vimos resultados parecidos em um trabalho semelhante em andamento no NJIT. Chamamos isso de contágio contextual de qualidade
A parte interessante aqui são as situações reais comuns na indústria: bases de código com qualidade mista, ou bases que misturam padrões legados com padrões “bons” mais novos, fazendo o agente confundir as convenções
O desenho de pares mínimos é, na verdade, um dos pontos fortes, porque tenta isolar a limpeza de outros fatores como estrutura, dependências e testes, em vez de comparar repositórios entre si
Ainda assim, usar código “bagunçado” gerado por LLM é um tanto questionável, por não ser um método mecânico nem orientado por humanos
A maior crítica, como outros apontaram corretamente, é a escolha de não verificar a suíte completa de testes. A alegação de “equivalência comportamental” só é tão válida quanto os testes e a cobertura
Essa hipótese é convincente por dois motivos: 1) LLMs imitam o que veem na base de código, então faz sentido que lixo entre, lixo saia; 2) isso bate com a intuição que muitos engenheiros tiveram ao usar esses modelos no último ano ou dois
Greenfield quase sempre é mais fácil do que entrar em uma base de código movimentada, e a bagunça surge em integrações complexas e na manutenção de sistemas com propósitos legados
Mesmo que o agente aprenda a lidar com os stubs e o código WET que ele próprio deixou, você realmente quer uma base de código em que pessoas não consigam acompanhar o que está acontecendo?
Por isso, pessoalmente, quero ao menos que o código conte em código o que ele faz
Mesmo que duas funções pequenas tenham uma lógica que poderia ser extraída para um helper compartilhado, um programador humano às vezes não faria isso porque sabe que a abstração ficaria bagunçada e quebraria se você quisesse mudar um pouco uma delas
É interessante ver isso quantificado
Uma estrutura limpa parece reduzir a carga cognitiva tanto de pessoas quanto de agentes, o que explica por que nomes e modularização são mais importantes do que parecem
É difícil quantificar, mas é isso que todas as métricas de qualidade de código tentam capturar no fim
Visto por esse critério, desde que a métrica de qualidade de código usada seja razoável, a conclusão não é tão surpreendente
Se a métrica de qualidade é boa no contexto de agentes de codificação, esse é exatamente o resultado esperado
Muitos tokens são gastos explorando o código: encontrando código ou seguindo pontos de chamada, no processo de criar contexto suficiente para executar a tarefa
Dar ao agente algum tipo de acesso a LSP e, no caso de um monorepo, fornecer orientação hierárquica com arquivos como AGENTS.md pode reduzir bastante o uso de tokens na exploração
Mas uma base de código espalhada acaba exigindo algum tipo de exploração para resolver cada tarefa
E essa exploração não é só uso de tokens. A cada etapa, repetem-se latência do LLM, prefill, decoding, saída, parsing pelo agente, chamada de ferramenta, resposta da ferramenta e a ida e volta de volta ao LLM
Parte disso pode ser paralelizada, mas na prática a maior parte é sequencial, então a tarefa fica bem lenta
Para usar agentes com eficiência, localidade e estrutura são fundamentais. A janela de contexto é sempre limitada, e a atenção dentro dela também não é consistente
Pela minha experiência, tudo que afeta engenheiros também afeta agentes
Boas abstrações, métodos de tamanho adequado, bons nomes, estrutura com princípios dentro dos serviços e entre serviços, testes unitários etc. entram nessa lista
Historicamente, essas coisas eram trabalho de engenharia e serviam para facilitar a contribuição de outras pessoas no código
Agora elas facilitam a contribuição não só de outras pessoas, mas também de outros agentes