Quando ler o código da IA não é revê-lo
Ouvir o artigo (experimental) · 10:01

Consigo imaginar com nitidez um programador, porque uma versão dele está por todo o lado neste momento, com um fluxo de trabalho que parece responsável. Deixa o modelo escrever o código e lê o diff antes de o aceitar. Está a rever. Só que os diffs vão ficando mais longos, a leitura vai ficando mais rápida e, numa tarde qualquer, dá-se conta de que anda a aceitar código que já não conseguiria alterar se fosse preciso. Os projetos que a máquina construiu na maior parte são aqueles em que mal se orienta. Um projeto que escreveu à mão há três anos continua a parecer-lhe a sua própria casa. Algures pelo meio, ler deixou de ser rever, e nada o anunciou.
Às vezes ler é o trabalho todo
Eis onde a versão forte deste aviso se engana. Para uma boa fatia do que aceitas num dia, ler o diff é o trabalho inteiro, e corrê-lo tu próprio seria uma encenação. Um rename que o compilador já provou. Uma subida de versão de uma dependência. Um valor de configuração, uma alteração de texto, um comentário. Boilerplate que já escreveste cem vezes e reconheces pela forma. Um bloco de testes gerados, onde ler é de qualquer modo a passagem mais valiosa, porque é a leitura que apanha o teste que não afirma nada e mesmo assim fica verde.
Ler com atenção não é um instrumento fraco. Muito antes de tudo isto, a inspeção de código encontrava mais defeitos por hora do que os testes, e continua a encontrar. Portanto a questão não é "corre sempre o código". A questão é que ler e correr são cegos a bugs diferentes, e a maior parte da discussão que as pessoas têm é, no fundo, uma discussão sobre dentro de que cegueira estão a viver.
O verdadeiro teste é se consegues prevê-lo
A pergunta nunca foi ler contra correr. É se consegues prever o que o código faz sem o correr.
Para uma categoria de mudança consegues, e ler chega. Para outra, a correção não está no texto à tua frente. Vive nas entrelinhas, no entrelaçar de duas threads que o diff só te consegue mostrar uma de cada vez. Vive sob carga, na query que está bem em dez linhas e é fatal em dez milhões. Vive na fronteira, no off-by-one e no caminho de erro por onde o caso feliz passa a direito. Vive na ligação, na questão de saber se a nova peça está sequer ligada àquilo que a chama. Vive no espaço negativo, na verificação de autorização que não existe e na exceção que é silenciosamente engolida, ou seja, numa linha que nunca aparece no diff porque nunca foi escrita. Não há leitura que te leve a um bug que só existe em runtime.
A IA faz "parece certo" valer menos
A IA agudiza isto de uma maneira muito específica. Quem revê aprova código com base em sinais que fazem as vezes de correção. Lê-se como idiomático. Parece confiante. Assemelha-se a código que já funcionou antes. Um modelo de linguagem é uma máquina afinada para produzir exatamente esses sinais. É muito bom a parecer certo.
Por isso o atalho mais antigo da revisão, "isto parece bem", nunca foi prova tão fraca de que a coisa está bem. O polimento não é garantia. É o risco, porque aponta precisamente à parte de ti que ia deixar passar a mudança. Há investigação que mostra que pessoas a quem se deu um assistente capaz escreveram código menos seguro e sentiram-se mais certas de que era seguro. A confiança move-se no sentido oposto ao do perigo, e código gerado e limpo é a entrada mais desarmante que se pode pôr nas mãos de quem revê.
A máquina também consegue corrê-lo
Correr o código já não é apenas algo que fazes à mão. O agente pode corrê-lo por ti e devolver-te os logs, um screenshot, um parágrafo a explicar o que mudou e por que motivo está confiante. Isso é real, e para muitas mudanças é o trabalho todo: os testes ficaram verdes enquanto os viste correr, o screenshot mostra o botão no sítio certo, e acabaste mais cedo do que acabarias sozinho.
Mas repara no que aconteceu ali. Essa prova foi produzida pelo mesmo tipo de sistema que escreveu o código, apontada ao mesmo alvo, que é parecer certo. Um log de sucesso que não viste correr é uma afirmação, não um facto. Podes até pôr o agente de vigia, sobre os logs, a base de dados, as chamadas que saíram, e perguntar se está tudo a bater certo, e isso é uma alavanca real. É também aí que já apanhei um modelo a não olhar a sério, a responder com aquilo que esperava encontrar. Por isso entregar a execução ao agente não elimina a revisão. Desloca-a um nível acima, do diff para a prova. Quando te disser que os logs mostram X, abre os logs e encontra X. Trata o "funcionou, aqui está a prova" como uma afirmação a confirmar, do mesmo modo que confirmarias um número antes de o deixares ficar num parágrafo que um modelo escreveu. É a mesma verificação que um investigador salta quando uma citação inventada acaba num artigo publicado.
O que nos leva à parte que quase tem piada. Para muitas mudanças pequenas, a coisa mais rápida e mais fiável que podes fazer é corrê-la tu próprio. Ler uma parede de logs e um resumo confiante pode custar-te mais do que abrir a coisa e olhar com os teus próprios olhos. Correr não é a opção lenta e conscienciosa a que recorres quando ler falha. Metade das vezes é a rápida.
A falha em que a maioria bate é o volume
A falha em que a maioria realmente bate não tem nada de subtil. É o volume.
Passadas umas centenas de linhas, qualquer revisão humana degrada-se num relance, e um agente dá-te umas centenas de linhas sempre que pedires. O trabalho só de revisão é a forma mais cansativa que o ofício assume, porque estás a reconstruir a intenção de outra pessoa, num estilo que não é o teu, a um ritmo que não definiste. Por isso desmorona-se, em silêncio, em aceitação. Ninguém decide parar de rever. Começas a ver o código em vez de o compreender, e por dentro os dois são idênticos. A restrição que importa não é quanto o modelo consegue escrever. É quanto consegues realmente segurar. Deixa-o escrever para lá disso e não estás a rever, seja lá o que o ecrã diga que estás a fazer.
Ajusta a profundidade ao alcance do impacto
Isso transforma a disciplina em algo mais útil do que "ler com mais atenção". Ajusta a profundidade da revisão ao alcance do impacto, e ao quão reversível é a mudança.
Uma mudança pequena, contida e fácil de reverter merece uma leitura rápida e um merge. Uma migração, uma interface pública, uma eliminação, tudo o que toca em dinheiro ou em acessos, ganha o tipo lento de revisão: corre-a, segue-a passo a passo, tenta parti-la, quer sejas tu a fazê-lo, quer ponhas o agente a mostrar o seu trabalho e depois verifiques o que ele fez de facto, não importa quem ou o que a escreveu. Ler chega exatamente quando as formas de a mudança falhar são as formas que os teus outros gates já apanham: o compilador, os tipos, os testes que correm no CI, os corras ou não à mão. Deixa de chegar no momento em que a falha que te iria mesmo magoar é uma que só correr a coisa revelaria. Aplicar o mesmo escrutínio a cada diff não é diligência. É uma recusa de decidir onde está o risco.
Aquilo de que abdicas
Há um custo mais silencioso por baixo do custo técnico. Aceitar código que não compreendeste desloca-te, um clique de cada vez, de autor do teu produto para seu consumidor. Ficas com o crédito e ficas com a responsabilidade, o pager continua a tocar para ti, mas abriste mão da compreensão em que ambos assentam. Há quem descreva construir uma app numa tarde e sentir-se estranhamente vazio, e depois passar duas semanas a pô-la mesmo a funcionar e voltar a sentir-se ele próprio. Isto não é nostalgia. É a diferença entre ser dono da coisa que leva o teu nome e segurar uma cópia que não consegues abrir.
Construo ferramentas que apostam totalmente na geração. Uma transforma uma frase num plano e abre os seus próprios pull requests. Outra governa frotas inteiras de agentes. Não estou aqui para dizer a ninguém que escreva menos código com IA. Mas cada uma dessas ferramentas pressupõe, lá no fim, uma pessoa que revê à profundidade que a mudança merece, e um gate por onde só espreitas não é um gate. A máquina consegue produzir o diff. Não consegue produzir a parte em que o compreendeste.
Por isso revê a olho à vontade, para as mudanças em que o olho é tudo o que há. Só tens de manter uma linha clara entre o diff que compreendeste e o diff que apenas viste. O primeiro é teu. O segundo passa a ser teu no instante em que se parte, e não vai querer saber que parecia estar bem.
Perguntas a que este artigo responde
Ler o diff chega para rever código gerado por IA?
Para uma boa fatia do que aceitas num dia chega, e corrê-lo seria uma encenação: um rename que o compilador já provou, uma subida de versão de uma dependência, um valor de configuração, uma alteração de texto, boilerplate que reconheces pela forma. Os testes gerados são o caso mais claro, porque é a leitura que apanha o teste que não afirma nada e mesmo assim fica verde. Deixa de chegar quando a correção não está no texto à tua frente: o entrelaçar de duas threads, a query que está bem em dez linhas e é fatal em dez milhões, o caminho de erro por onde o caso feliz passa a direito, ou a verificação de autorização que nunca aparece no diff porque nunca foi escrita.
O agente correu os testes e deu-me logs verdes e um screenshot, porque hei de verificar mais alguma coisa?
Porque essa prova foi produzida pelo mesmo tipo de sistema que escreveu o código, apontada ao mesmo alvo, que é parecer certo. Um log de sucesso que não viste correr é uma afirmação, não um facto. Entregar a execução ao agente não elimina a revisão, desloca-a um nível acima, do diff para a prova: quando te disser que os logs mostram alguma coisa, abre os logs e encontra-a. Já apanhei um modelo a não olhar a sério e a responder com aquilo que esperava encontrar, e é a mesma verificação que um investigador salta quando uma citação inventada acaba num artigo publicado.
Quanto código de IA consigo mesmo rever antes de deixar de ser revisão?
Menos do que um agente te vai entregar, porque ele dá-te umas centenas de linhas sempre que pedires e, passado esse ponto, qualquer revisão humana degrada-se num relance. O trabalho só de revisão é a forma mais cansativa que o ofício assume, porque estás a reconstruir a intenção de outra pessoa, num estilo que não é o teu, a um ritmo que não definiste. Ninguém decide parar de rever. Começas a ver o código em vez de o compreender, e por dentro os dois são idênticos.
Não é mais seguro rever tudo com a mesma profundidade?
Não. Aplicar o mesmo escrutínio a cada diff não é diligência, é uma recusa de decidir onde está o risco. Ajusta a profundidade ao alcance do impacto e ao quão reversível é a mudança: uma mudança pequena, contida e fácil de reverter merece uma leitura rápida e um merge, enquanto uma migração, uma interface pública, uma eliminação ou tudo o que toca em dinheiro ou em acessos ganha o tipo lento, em que a corres, a segues passo a passo e tentas parti-la. Corrê-la tu próprio também não é a opção lenta e conscienciosa, porque abrir a coisa e olhar é muitas vezes mais rápido do que ler uma parede de logs e um resumo confiante.
Ler a seguir
AIQuando a IA tem de filmar a sua própria revisão
Quando é aberto um pull request, meu ou um que tenho de rever, passei a pedir ao agente que o corra na minha máquina, que tente parti-lo e que se filme a fazê-lo. Um parágrafo pode afirmar que houve um teste. Uma gravação tem de o mostrar, e decidir o que isso significa continua a ser trabalho meu.
AIA imagem que tenho na cabeça. A soma de um ano a construir e a escrever
Vinte e seis ensaios, seis produtos, uma prática de consultoria e uma época a medir coisas que podia ter apenas afirmado. Relidos como um único trace, correm todos o mesmo mecanismo: a IA torna gratuita a metade barata de tudo, e a metade cara muda de morada. Este é o mapa das moradas (julgamento, resposta, atenção, a pessoa), e a razão pela qual a minha fé reconheceu o território antes de mim.