Engenharia

O reviewer mais duro do time não é humano

O melhor contribuidor de um codebase é o que diz "ainda não, você não testou isto".

ASR

Apollo Space Research

Apollo Space

· 11 min de leitura

A coisa mais produtiva que aconteceu no nosso codebase na semana passada foi uma rejeição. Um agent tinha escrito uma mudança limpa, os testes estavam verdes, o diff era pequeno e legível, e outro agent leu tudo e devolveu com uma linha: ainda não, você não testou isto. Nenhum código novo. Nenhum insight esperto. Só uma recusa, entregue no instante em que o trabalho parecia pronto.

Essa recusa vale mais que o código que ela bloqueou. E o agent que a escreveu não produz quase nada o dia inteiro.

Aqui está a ideia sobre a qual este post fala, dita de forma simples para que você possa levá-la porta afora: o melhor contribuidor de um codebase é o que diz “ainda não, você não testou isto”.

A maioria dos times trata review como o imposto que você paga antes de shipar. Nós o tratamos como o único lugar onde qualidade é de fato decidida. Esta é a história de por que demos esse trabalho a uma máquina, por que a máquina é mais difícil de satisfazer que qualquer humano que poderíamos contratar, e o que isso nos comprou que “escritores mais espertos” nunca conseguiriam.

A versão ingênua: shipar no verde

O jeito óbvio de construir software com agents é o jeito que as demos mostram. Um agent capaz pega uma task, a planeja, escreve o código, roda a suíte de testes, vê verde e reporta: pronto. Se os testes passam, você shipa. Parece o futuro, e por uma tarde é.

Então você bate na coisa que ninguém coloca na demo.

A falha não é que o agent escreve código ruim, o código geralmente está bom. A falha é mais sutil e pior: o agent que escreveu o código é o mesmo que o avalia. Ele escreveu o teste, então o teste assere exatamente o que o código já faz. Ele decidiu o que “pronto” significava, então “pronto” virou o que quer que ele tenha terminado. Ele nunca testou o input que não imaginou, porque se tivesse imaginado aquele input, teria tratado. Testes verdes numa mudança autoavaliada não provam que a mudança está correta. Provam que o autor concordou consigo mesmo.

Todo time de engenharia aprendeu essa lição do jeito difícil e construiu o mesmo fix. Não engenheiros mais espertos, um segundo par de olhos sem participação no trabalho. Code review existe porque o autor é a pior pessoa possível para certificar o próprio código. O orgulho que fez o trabalho ficar bom é exatamente o orgulho que torna a avaliação não confiável.

Então o fix ingênuo que todo mundo busca em seguida é adicionar um agent reviewer. Sensato. Também não funciona, e vale ver por quê antes que a versão de verdade faça sentido.

Por que um segundo agent educado não vale nada

Adicione um agent que confere o trabalho do escritor. Pergunte a ele: “isto parece correto?” Veja o que acontece.

Ele concorda. Quase sempre. A mudança é plausível, os testes estão verdes, a explicação do escritor é coerente e confiante, então o reviewer, ao receber uma pergunta de sim-ou-não com uma resposta claramente preferida, diz sim, parece correto. Você não adicionou uma segunda opinião. Você adicionou uma segunda assinatura no mesmo ponto cego. Dois agents agora acreditam que o trabalho está pronto. Nenhum deles tentou provar que não está.

Essa é a armadilha que torna a maioria das features de “reviewer de IA” teatro. Um reviewer sem mandato adversarial é um carimbo de borracha com latência extra.

O fix não é um reviewer melhor. O fix é uma pergunta diferente.

Não perguntamos ao nosso reviewer “isto está certo?” Perguntamos “como isto falharia na frente de um usuário real, e onde está o flow que prova que não falha?” Esse reframe é o mecanismo inteiro. Um agent mandado a caçar a falha vai e encontra a falha, o input não tratado, o caminho que nenhum teste exercitou, as duas coisas que acontecem ao mesmo tempo. E um agent mandado a exigir prova para de aceitar explicações e começa a exigir um flow que de fato rodou. O reviewer não está sendo difícil. Ele é estruturalmente capaz da única coisa que o autor não pode ser: cético de um trabalho no qual não tem orgulho nenhum.

À esquerda, um agent escreve código, roda o próprio teste, vê verde e dá merge no próprio trabalho, então o bug shipa; à direita, um reviewer que nunca escreveu o código pergunta qual flow real provou a mudança, o roda, anexa o trace, e dá merge só num resultado.

O melhor contribuidor de um codebase é o que diz “ainda não, você não testou isto”. Não porque é mais esperto que o escritor. Porque é o único na sala que não está tentando estar pronto.

Uma afirmação não é um resultado

Aqui está a linha que nosso reviewer foi construído para segurar, e é o padrão inteiro comprimido em uma frase: “funciona” é um sentimento, e um sentimento não é um resultado.

“Funciona” é algo em que você acredita. “Este flow exato rodou de ponta a ponta, e aqui está o trace” é algo que você consegue conferir.

Veja com que frequência os dois se confundem. Uma mudança chega vestindo todo sinal de prontidão, checks verdes, um diff arrumado, um resumo confiante que diz que a feature está completa. Nada disso é evidência de que a feature faz o que um usuário precisa. É evidência de que o autor acha que faz. O gap entre esses dois é exatamente onde bugs shipados vivem, e é invisível para quem lê o resumo em vez de rodar o flow.

Então nosso reviewer recusa o resumo e exige o run. Não “os testes passam”, mas qual teste, contra que input, provando que comportamento de que uma pessoa de verdade se importaria. Uma afirmação sem nenhum flow executável por trás é tratada como nenhuma afirmação, o mesmo que silêncio. Isso soa duro. É a dureza mais barata que dinheiro pode comprar.

Pense em onde um bug de fato te custa. Pego por um reviewer que roda em segundos, é uma frase de feedback. Pego em produção, é um incidente, um rollback, um usuário que aprendeu a não confiar na coisa, e uma tarde que ninguém recuperou. O reviewer que insiste num run real antes do merge não está te atrasando. Está movendo a captura do lugar mais caro para o mais barato. Essa troca só parece overhead se você nunca pagou o preço de produção, e todo mundo lendo isto já pagou.

A disciplina tem um nome que usamos internamente e não é glamouroso: um teste que falta não é uma feature que falta, é uma prova que falta. O comportamento pode ser perfeito. Você só não o mostrou, e “tenho certeza que está tudo bem” shipou mais outages do que maldade jamais shipou.

O reviewer que roda o flow, não o teste

O reviewer ingênuo lê o diff. O nosso roda o software.

Ler um diff te diz que o código é plausível, que ele compila na sua cabeça, que a lógica parece sólida, que o autor foi cuidadoso. Não pode te dizer a coisa que você de fato precisa saber, que é o que acontece quando o sistema real executa esta mudança contra um input real numa superfície real. Essas são perguntas diferentes, e só uma delas é a verdade.

Então o reviewer mais forte não avalia a descrição do trabalho. Ele reproduz o trabalho. Pega a mudança, roda o flow genuíno que um usuário dispararia, o caminho multi-step, aquele em que o passo dois tem que lembrar do que o passo um fez, e observa o que o sistema de fato faz. Pass significa que o flow rodou e se comportou. Não “o autor diz que se comporta”. Ele se comportou, e há um trace para provar.

Uma afirmação de "funciona" é interrogada, não confiada: o reviewer exige que o flow real seja rodado numa superfície real, o run produz um trace em vez de uma opinião, e o veredito é ou um pass ou "ainda não, você não testou isto", devolvido direto para o escritor.

Essa é a diferença entre um reviewer que pode ser convencido a um sim e um reviewer que não pode. Você consegue escrever uma explicação linda que convence um leitor de diff. Você não consegue convencer um flow a rodar verde. O software ou fez a coisa ou não fez, e o reviewer que insiste em observá-lo fazer a coisa é imune a um autor persuasivo de um jeito que nenhum humano sob pressão de prazo é de forma confiável.

Essa imunidade é o ponto. Humanos se cansam. Humanos querem ser gentis. Humanos têm um prazo hoje à noite e um escritor que claramente trabalhou duro e uma voz baixinha que diz provavelmente está tudo bem, só dá merge. O reviewer máquina não tem prazo, nem fadiga, nem desejo de ser querido. Às 2h da madrugada na noite antes de uma demo, quando todo instinto humano é deixar a mudança passar, ele faz a mesma pergunta seca que fez ao meio-dia: onde está o flow que prova isto? O melhor contribuidor de um codebase é o que diz “ainda não, você não testou isto”, e ele nunca uma vez se cansa de dizer.

Quanto custa, honestamente

Isso não é de graça, e fingir que é seria seu próprio tipo de desonestidade.

Custa compute. Rodar um reviewer que reproduz o flow real, toda vez, em toda mudança, queima mais que um único agent que shipa no verde. Gastamos isso deliberadamente. A coisa cara em software nunca foi a digitação, foi a regressão que se escondeu por uma semana, o parece pronto que não estava, a confiança que um usuário gasta uma vez e não reembolsa. Um adversário que roda em segundos é o lugar mais barato da terra para pegar isso, e preferimos pagar lá do que num canal de incidente.

O que isso compra é uma propriedade genuinamente difícil de conseguir de outro jeito: o output é confiável sem um humano avaliando cada linha. Não porque os escritores são impecáveis, não são, e não precisam ser. A confiança não vive na confiabilidade de nenhum agent. Ela vive na estrutura, no fato de que nada chega à branch principal sem sobreviver a um agent construído para quebrá-lo e a um agent que se recusa a aceitar um sentimento como resultado. Essa é a mesma razão pela qual um projeto open-source sério pode aceitar código de um estranho: ele não confia no estranho, ele confia no review.

E os escritores ficam cada vez melhores, o que não muda nada sobre por que isto importa. Um escritor mais esperto produz afirmações mais convincentes. Não produz afirmações mais provadas. Quanto mais esperto o autor, mais você precisa de um reviewer que avalia o run em vez da retórica.

A virada: um padrão é algo que você constrói

Tire os agents e o que sobra é a coisa verdadeira mais antiga sobre construir qualquer coisa bem.

Qualidade nunca foi uma propriedade das pessoas fazendo o trabalho. É uma propriedade do processo que decide o que tem permissão para contar como pronto, e os melhores times sempre souberam que o padrão não vive na cabeça de ninguém, ele vive no review que toda mudança tem que passar, aquele que segura mesmo na noite em que todo mundo está exausto e o prazo é agora. Esse padrão é a coisa que você não consegue instalar de um package manager. Você pode comprar escritores mais rápidos e modelos mais espertos o dia inteiro; nenhum dos dois vai, às 2h da madrugada, olhar para um trabalho brilhante e dizer ainda não.

Não inventamos essa disciplina. Notamos que ela se transfere. Um agent pode escrever código, um agent pode atacar código, e um agent pode segurar a linha sobre o que tem permissão para dar merge, e quando você dá o trabalho de segurar a um reviewer que nunca se cansa e nunca quer ser querido, a linha segura na pior noite, que é a única noite em que ela algum dia importou.

A coisa que torna um codebase bom nunca foi a contribuição mais esperta. Foi a rejeição que manteve a contribuição mais esperta honesta. O dia em que deixarmos o reviewer se cansar é o dia em que a podridão começa, e a razão inteira de fazer esse reviewer uma máquina é que uma máquina nunca está cansada.


É isso que estamos construindo na Apollo Space: um sistema operacional onde agents não só fazem o trabalho, eles seguram uns aos outros a um padrão que ninguém está cansado demais para defender. Se você já deu merge num parece pronto que não estava e pagou por isso numa sexta à tarde, você já sabe por que o reviewer mais duro do time nunca deveria ser o que escreveu o código.

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