Engenharia

Um engenheiro sênior não lê o código. Seu agent também não deveria.

Um agent jogado numa codebase que nunca viu vence dando grep no ponto de costura, desenhando o mapa e lendo três arquivos, não lendo todos eles.

ASR

Apollo Space Research

Apollo Space

· 12 min de leitura

Entregue a um engenheiro sênior recém-contratado um repositório com quatrocentos arquivos e um bug para corrigir, e observe o que ele faz nos primeiros dez minutos. Ele não abre o arquivo um e começa a ler. Ele roda uma busca. Olha os nomes das pastas. Encontra a única função perto da qual o bug vive, lê quem a chama e ignora os outros trezentos e noventa arquivos por completo. Vinte minutos depois ele tem uma correção. Ele nunca leu a codebase.

Entregue a mesma tarefa a um agent ingênuo e ele tenta ler tudo. Ele falha por uma razão que não tem nada a ver com inteligência.

Um engenheiro sênior não lê o código, ele encontra o ponto de costura, desenha o mapa e então lê os três arquivos que importam. Essa é toda a habilidade, e é a que um agent precisa aprender antes de tocar numa linha. Este post é sobre como você ensina uma máquina a fazer aquilo que os melhores engenheiros fazem sem pensar.

A forma ingênua: ler tudo, depois responder

A abordagem óbvia é a que parece responsável. Você quer que o agent corrija um bug corretamente, então dá a ele o código. Todo ele. Enfia o repositório na context window, deixa o modelo ver a coisa inteira e pede que ele raciocine sobre o quadro completo.

Isso quebra de duas formas, e ambas são fatais.

A primeira é mecânica. Uma codebase real de empresa não cabe. Mesmo com uma context window grande, um repositório sério é maior que a janela, e a metade que não cabe é, pelas leis do azar, a metade onde o bug vive. Você está pedindo ao modelo que raciocine sobre código que ele não consegue ver. Ele vai raciocinar com confiança mesmo assim, o que é pior do que admitir que não consegue.

A segunda é mais sutil e sobrevive mesmo quando o código cabe. Um modelo a quem você entrega dez mil linhas não lê como um engenheiro. Lê como um estudante enchendo a cabeça para uma prova, cada linha pesada mais ou menos igual, a função crítica enterrada na mesma camada plana de atenção que o helper de logging com que ninguém se importa. O sinal está lá. Só está se afogando. Dê demais a um modelo e você não obtém uma resposta minuciosa; obtém uma vaga, porque nada se destacou.

Então o instinto ingênuo, dê mais contexto, obtenha uma resposta melhor, está exatamente invertido. Mais código é mais ruído. O trabalho não é alimentar o modelo com a codebase. O trabalho é alimentá-lo com os três arquivos que importam e nada mais.

O que levanta a única pergunta de verdade: como você encontra os três?

Grep primeiro: o ponto de costura é uma string

Aqui está o movimento que o engenheiro sênior faz e que o agent ingênuo pula. Antes de ler qualquer coisa, ele busca.

Ele não busca por entendimento. Ele busca por um ponto de costura, o lugar exato onde a mudança tem de acontecer. Um relato de bug diz “o botão de exportar não faz nada”. O engenheiro não lê a feature de exportação. Ele dá grep na string do botão, encontra o único arquivo que o renderiza, encontra o handler que ele chama, e agora está em cima do ponto de costura. Total de arquivos lidos: cerca de dois. Tempo total: alguns minutos.

O agent ingênuo, em contraste, raciocina em direção à feature de exportação de cima para baixo, bem, exportações provavelmente vivem numa camada de services, que provavelmente chama um renderer, e metade das vezes o palpite está errado, porque toda codebase se organiza de forma diferente. Raciocinar sobre onde o código deveria estar é um palpite. Buscar onde ele está é um fato.

Esta é a primeira lei de ler uma codebase que você nunca viu: a estrutura é desconhecida, mas as strings não são. Um nome de função, uma mensagem de erro, uma rota, um label num botão, esses são âncoras que existem em exatamente um ou dois lugares, e uma busca te deixa em cima deles instantaneamente sem você entender nada antes. O entendimento vem depois que você pousou, não antes.

Então a primeira ferramenta do agent não é um leitor. É uma busca. Dê a ele grep, dê a ele o símbolo, e ele se teleporta para o ponto de costura do jeito que um engenheiro faz, não por conhecer o mapa, mas por conhecer a palavra mágica escrita em exatamente a porta de que ele precisa.

Duas formas de um agent encontrar o lugar para corrigir um bug. O caminho ingênuo lê cada arquivo de cima a baixo e raciocina sobre onde o código deveria viver, afogando-se em ruído. O caminho grep-primeiro busca pelo símbolo exato ou string de erro, pousa diretamente no único ponto de costura e lê apenas seus vizinhos imediatos.

Estrutura antes do detalhe: construa o mapa, depois dê zoom

Grep te deixa num ponto. Um ponto não é entendimento. O engenheiro que encontrou o handler de exportação ainda não sabe se pode alterá-lo com segurança, porque ainda não sabe quem mais o chama, do que ele depende e o que quebra se ele o tocar.

A falha do agent ingênuo aqui é o espelho da última. Tendo encontrado um arquivo relevante, ele mergulha direto no detalhe profundo daquele arquivo, lendo cada linha, cada helper, cada import, e perde o fio. Ele consegue te dizer o que a linha 200 faz. Não consegue te dizer se mudar a linha 200 vai quebrar as três outras features que silenciosamente dependem dela. Ele deu zoom in antes de dar zoom out.

O engenheiro sênior faz o oposto, e a ordem é todo o truque. Estrutura antes do detalhe. Antes de ler o corpo da função, ele faz as perguntas estruturais baratas: o que chama isso? o que isso chama? qual é o formato da pasta em que vive? Essas respostas formam um mapa, um grafo aproximado da vizinhança, e o mapa te diz qual detalhe vale a pena ler e qual é seguro pular.

A forma ingênua é entender um arquivo lendo-o. A forma que escala é entender um arquivo pela sua posição, quem o chama, o que ele chama, a intenção do diretório, e só então ler as poucas linhas onde a mudança de fato pousa. Uma função que você localizou num mapa é uma função que você pode mudar com segurança. Uma função que você apenas leu é uma função que você pode quebrar em três lugares em que nunca olhou.

Então o segundo instinto do agent, depois que o grep o deixa no ponto de costura, é caminhar para fora um salto. Quem importa isto. O que isto importa. Arquivos irmãos com o mesmo formato. Ele constrói o mapa antes de tocar numa linha, e o mapa, não o texto completo, é o que permite que ele responda a pergunta que de fato importa: se eu mudar isto, o que mais se move?

É também aqui que o agent ganha o direito de discordar de si mesmo. Um mapa torna um plano errado visível. Se a correção que o agent primeiro imaginou fosse ondular em quatro callers não relacionados, o mapa mostra isso antes de uma única linha mudar, e a coisa barata de revisar é o plano, não o banco de dados de produção.

Leia três arquivos, não trezentos

Agora o agent lê. Mas ele lê do jeito que um engenheiro sênior lê no primeiro dia de um emprego novo, estreitamente, e de propósito.

A ambição ingênua é completude: ler o suficiente para que você entenda o sistema inteiro. Essa ambição é a armadilha. Ninguém entende o sistema inteiro, nem o engenheiro sênior, nem a pessoa que escreveu metade dele, certamente não na primeira manhã. O que o engenheiro sênior tem e que o agent-recém-chegado precisa aprender é a confiança de deixar a maior parte do código sem ler, de ler exatamente os arquivos que o mapa sinalizou como load-bearing para esta mudança, e de confiar que o resto é problema de outra pessoa até deixar de ser.

Três arquivos, escolhidos pelo mapa, batem trezentos lidos às cegas. O arquivo do ponto de costura, onde a mudança pousa. Seu caller mais próximo, para você saber o que espera o comportamento antigo. Um irmão do mesmo formato, para você copiar as convenções reais da codebase em vez de inventar as suas. Essa costuma ser toda a lista de leitura para uma mudança contida, e é a mesma lista que o engenheiro teria construído.

Note o que isso faz com o custo. Imagine, puramente como ilustração, um repositório onde ler tudo significaria engolir o equivalente a um pequeno romance, a maior parte dele irrelevante para a tarefa. O caminho grep-depois-mapa-depois-três-arquivos lê, digamos, algumas páginas em vez disso. A questão não é a economia exata. A questão é que o caminho barato e o caminho correto são o mesmo caminho. Ler menos não é um meio-termo que você faz por velocidade; é como bons engenheiros chegam à resposta certa, porque a resposta certa sempre esteve escondida em uns poucos arquivos, e os outros trezentos nunca foram nada além de ruído.

Um funil do repositório inteiro até a mudança. Quatrocentos arquivos se estreitam por um grep que pousa num ponto de costura, depois um mapa estrutural dos vizinhos desse ponto, até três arquivos de fato lidos, terminando numa única mudança direcionada que respeita as próprias convenções da codebase.

Por que isso é mais difícil para um agent do que para um humano

Há uma objeção honesta aqui: um humano faz tudo isso por instinto. O agent tem de ser construído para fazer. E a construção é a parte interessante, porque o instinto é feito de três hábitos que não vêm de graça num modelo de linguagem.

O primeiro hábito é contenção. O default de um modelo é consumir tudo o que é oferecido e responder a partir de tudo. Ler-como-um-engenheiro significa dar ao agent ferramentas que o deixam escolher o que olhar, buscar, listar um diretório, seguir um import, e uma disciplina que recompensa olhar menos. A habilidade não é ler. É decidir o que não ler.

O segundo é sequência. Grep, depois mapa, depois ler, depois mudar, nessa ordem. Um modelo deixado por conta própria vai alegremente pular para a mudança, escrever um patch plausível e nunca checar quem mais dependia da coisa que ele acabou de alterar. A ordem não é burocracia. A ordem é a diferença entre uma correção e um novo bug que parece uma correção.

O terceiro é humildade quanto ao mapa. O mapa que o agent constrói é um esboço, não uma prova, e o agent tem de saber a diferença. Quando o esboço diz “esta função tem um caller” e a realidade tem um segundo caller que a busca perdeu, o instinto do engenheiro é desconfiar de um mapa limpo demais e olhar de novo. Um agent tem de receber o mesmo reflexo, tratar seu próprio modelo da codebase como uma alegação a verificar, não um fato sobre o qual agir. O mapa é uma ferramenta para encontrar os três arquivos. Não é um substituto para lê-los.

Junte esses três hábitos e você tem algo próximo de como um engenheiro lê código desconhecido: buscar para pousar, mapear para se orientar, ler estreitamente para entender, mudar com cuidado, e nunca confiar no mapa mais do que no território. Nada disso é o modelo sendo esperto. Tudo isso é o modelo sendo disciplinado, o que, numa codebase real, bate esperto toda vez.

A virada: a habilidade nunca foi ler

Tire os agents e olhe o que sobra, porque isso é mais velho que os agents.

O melhor engenheiro com quem você já trabalhou não foi o que tinha a codebase inteira na cabeça. Ninguém tem, e os que afirmam ter costumam estar errados sobre a parte que te morde. O melhor engenheiro foi o que conseguia ser jogado num sistema que nunca tinha visto, numa stack que conhecia pela metade, e encontrar o ponto de costura numa tarde, não por conhecer o código, mas por saber ler código: onde buscar, o que ignorar, quais três arquivos contavam a história, quando desconfiar do próprio primeiro palpite.

Essa habilidade sempre foi portátil. Nunca foi sobre um repositório em particular. Era uma forma de abordar o desconhecido, não assuma nada, busque pelo fato, desenhe o mapa barato, leia o pouco que importa, mude com cuidado, verifique. Ensine isso a um agent e você não obtém uma ferramenta que memorizou seu código. Você obtém algo que pode entrar num sistema que nunca viu, o seu, o do seu fornecedor, o que um desenvolvedor que saiu levou na cabeça, e começar a ser útil naquela mesma hora, do jeito que um ótimo recém-contratado faz, sem que ninguém primeiro o sente para ler quatrocentos arquivos.

Um engenheiro sênior não lê o código, ele encontra o ponto de costura, desenha o mapa e então lê os três arquivos que importam. A razão pela qual isso importa para uma empresa não é que seu agent fica mais rápido. É que o conhecimento de como seus sistemas de fato funcionam para de viver na memória de uma pessoa e começa a ser algo que qualquer mão capaz, humana ou não, pode recuperar lendo bem.


Isto é parte do que estamos construindo na Apollo Space, agents que leem uma codebase do jeito que um engenheiro sênior lê, para que “ninguém aqui entende mais aquele serviço” deixe de ser uma frase que alguém precise dizer. Se você já herdou um sistema sem mapa e encontrou o bug mesmo assim, você já sabe que a habilidade nunca foi ler cada linha. Era saber quais três linhas ler.

A Apollo cuida da operação repetitiva da sua empresa pro seu time não precisar.

Entre na lista de espera: acesso antecipado, preço de usuário fundador e um lugar na primeira fila enquanto a gente constrói.

Entrar na lista de espera