1 pontos por GN⁺ 3 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • 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.md para 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
    • 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
    • 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 switch de 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,5
    • sonar-caas-poc*: 16 / 855 issues, densidade de issues 0,61 / 27,16, densidade de complexidade cognitiva 179,8 / 218,9
    • sonarcloud-codedatalake*: 199 / 1.319 issues, densidade de issues 4,36 / 34,39, densidade de complexidade cognitiva 34,0 / 216,5
    • commons-bcel: 694 / 2.711 issues, densidade de issues 12,60 / 49,46, densidade de complexidade cognitiva 102,8 / 108,3
    • genie: 152 / 1.262 issues, densidade de issues 1,28 / 10,81, densidade de complexidade cognitiva 22,2 / 23,5
    • ckan: 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 /app difere
  • 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
  • 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

 
GN⁺ 3 시간 전
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

    • Se você comparar projetos bagunçados e bem estruturados pelo número de testes que falham, a taxa de sucesso pode ficar enviesada a favor da base de código bagunçada, que provavelmente já tem cobertura de testes e robustez menores desde o início
      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
    • Mesmo que a conclusão se sustente, no máximo ela é um melhor caso para código degradado
      Ela só mostra uma situação em que o código parece funcionalmente ok, mas custa mais tokens para concluir a tarefa
    • Se não controlam a quebra dos testes da aplicação, é basicamente um estudo sem valor
      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

    • Problemas como remoção de código morto, duplicação de código e código inalcançável, mencionados acima, já vêm sendo resolvidos há muito tempo por linters determinísticos na maioria dos ecossistemas de linguagem
      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-...
    • Lido com essas ferramentas há bastante tempo e, sempre que as tratei como se fossem pessoas, o desempenho pareceu melhorar
      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
    • Tenho uma sensação parecida quando trabalho em bases de código bagunçadas
      Em algum momento, aqueles padrões horríveis começam a grudar em mim também
    • Li concordando, mas pensando por que isso soa como um comentário ruim: essas afirmações anedóticas estão no lado oposto de uma discussão científica
      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
    • Isto também é mais uma impressão, mas sou tão preocupado que faço refatorações e passagens de limpeza de código com frequência e nunca as deixo de lado, então é difícil dizer com certeza se há de fato uma diferença de desempenho
      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 /src inchou 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

    • Sim. Só pedir a ferramentas de codificação com agentes para limpar a base de código, fazer refatorações direcionadas e aplicar princípios SOLID e boas práticas já produz muitas melhorias fáceis
      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
    • Pedir para aplicar o princípio YAGNI também parece funcionar bem para reduzir a base de código
      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
    • Entendo perfeitamente fazer isso de forma incremental, mas fazer tudo de uma vez em qualquer base de código em produção parece extremamente arriscado
      Especialmente se não houver testes ponta a ponta cuidadosos para o sistema inteiro
    • Só dizer “refatore a base de código” também funciona muito bem
      De todo modo, as regras de estilo de código já estão no CLAUDE.md
    • A palavra que você está procurando é idiomático
  • 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

    • Sou o primeiro autor. Quero esclarecer uma coisa
      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...
    • Você confiaria se fosse um repositório limpo bagunçado pela IA?
  • 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

    • O LLM não precisa ter a base de código inteira no contexto
      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?

    • Mesmo que o agente cuide de tudo, o inglês descreve de forma imprecisa o que o código faz
      Por isso, pessoalmente, quero ao menos que o código conte em código o que ele faz
    • Já vi o contrário: casos em que se força DRY demais
      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

    • Acho que a qualidade do código deveria ser definida, no fim das contas, por quão fácil é alterá-lo corretamente
      É 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