O problema não é a IA escrever código errado, é ela decidir sem contexto suficiente
Pedir para um agente de IA "resolver a atividade X" parece simples até você lembrar como a maioria das atividades são escritas: um título, duas linhas de descrição, talvez um critério de aceite genérico. Suficiente para um humano que já conhece o sistema preencher as lacunas com contexto tácito. Insuficiente para uma IA, que vai preencher exatamente as mesmas lacunas, só que adivinhando, e sem avisar que estava adivinhando.
O sintoma não é código malformado. É decisão de design tomada silenciosamente, no meio da implementação, sem ninguém saber que ela foi tomada. Quando isso acontece, o bug não aparece no PR, aparece em produção, ou pior, nem aparece: só fica lá, dormente, até o dia em que o caminho que a IA decidiu ignorar for exatamente o caminho que um cliente real vai percorrer.
A resposta que organizou esse problema, na prática, foi inverter a ordem: gerar um ADR (Architecture Decision Record) e um SDD (Solution Design Document) junto com o agente, antes de qualquer linha de implementação, obrigando a IA a ler o código real do sistema antes de decidir qualquer coisa, e só depois disso liberar a implementação. TDD entrou como uma terceira camada, quase inevitável: se já existe uma decisão registrada (ADR) e um desenho de como implementá-la (SDD), o próximo passo natural é converter isso em testes que falham pela razão certa, antes do código que os faz passar.
O nome que ficou pra esse fluxo foi "Plan first, then implement": ADR → SDD → TDD como um contrato de contexto entre o desenvolvedor e a IA, revisável em cada etapa, antes de qualquer commit.
Por que três camadas, e não uma
Cada documento resolve uma pergunta diferente, e tentar resolver as três com um único artefato é o motivo pelo qual "documentação" costuma ser ignorada por quem já viu documentação ruim:
- ADR — por quê. Uma decisão já tomada, registrada com alternativas rejeitadas e consequências assumidas. Não é um lugar para debate aberto (isso seria uma RFC); é o registro de algo que o time já decidiu, para que ninguém precise reconstruir o raciocínio depois.
- SDD — como. O desenho concreto: quais arquivos mudam, qual o contrato de entrada/saída, quais consumidores existentes são afetados. É aqui que "vou fazer um fallback entre duas fontes de dados" se transforma em "estas duas classes de apresentação acessam campos fixos, então o fallback sem tradução de formato produz uma resposta quase vazia".
- TDD — o quê, verificável. Os ciclos RED-GREEN que provam que a decisão do ADR e o desenho do SDD realmente se sustentam contra casos reais, incluindo os casos que só o SDD revelou.
A progressão importa: pular direto de ADR para código, sem o SDD, significa que decisões de mapeamento de dados, que não são arquiteturais o suficiente para um ADR, mas são grandes demais para ficar implícitas no código, não têm onde morar. Elas acabam vivendo na cabeça de quem implementou, exatamente o problema que o método existe para evitar.
O caso real: um fallback bidirecional entre duas APIs externas de consulta
A atividade original pedia um fallback: se a API principal de consulta a um cadastro externo falhasse, o sistema deveria tentar uma API secundária antes de desistir. Descrita assim, parece um try/catch com uma segunda chamada dentro. O ciclo ADR → SDD → TDD revelou que não era.
O que o ADR fixou: a decisão de qual API é primária, por que a ordem importa (SLA, formato de resposta, cobertura de dados), e, decisão que só apareceu numa segunda iteração do documento, que o fallback deveria disparar apenas em exceção, nunca em resposta de negócio malformada. Essa decisão existe porque as duas APIs sinalizam erro de forma assimétrica: uma sempre lança exceção, inclusive para "não encontrado"; a outra devolve sucesso HTTP com um indicador de erro no corpo da resposta. Sem essa distinção registrada explicitamente, um fallback "ingênuo" trataria uma resposta de erro de negócio da segunda API como sucesso.
O que o SDD expôs, que o ADR não tinha capturado:
- Tradução de campos é pré-requisito, não etapa opcional. As classes de apresentação que consomem cada uma das duas respostas acessam nomes de campo fixos, e cada API tem seu próprio formato. Um fallback que só troca a fonte de dados, sem normalizar o formato de resposta, produz um objeto quase vazio no formato que o resto do sistema espera, o fallback "funciona" no sentido de retornar sucesso, e falha silenciosamente no sentido que importa.
- Um blocker real em regra de negócio existente. Uma validação de um fluxo de baixa cadastral implementa uma regra que impede a baixa de registros ativos. Essa regra depende de um campo que, sem a tradução do item 1, simplesmente não chega, o que significa que a regra de negócio pararia de executar silenciosamente no caminho de fallback, sem nenhum erro visível.
- Código morto revelado como efeito colateral. Uma verificação de um campo de status dentro do mesmo fluxo de validação nunca é verdadeira para respostas da API primária, porque essa API nunca produz esse campo. Não fazia parte do escopo da atividade, foi descoberto porque mapear os dois formatos de resposta lado a lado, exigência do SDD, expôs a inconsistência.
Nenhum desses três achados estava na atividade original. Nenhum deles seria óbvio pedindo direto "implemente o fallback" para um agente. Os três só apareceram porque o processo de escrever o SDD forçou a leitura comparada dos dois formatos de resposta e dos consumidores reais desses dados, não porque alguém foi procurar proativamente por bugs.
Os cinco ciclos de TDD, e por que o quarto é o mais interessante
Com ADR e SDD fechados, o TDD deixou de ser "escrever teste antes do código" em abstrato e virou uma lista concreta de comportamentos a provar, na ordem em que o SDD os expôs: tradução de campo, disparo de fallback só em exceção, preservação da regra de negócio de baixa, e o tratamento do código morto identificado.
O ciclo mais didático não foi o mais complexo tecnicamente, foi o que tratou o código morto. A lição ali é que RED também pode significar "isto não deveria mais existir", não só "isto ainda não existe". Um ciclo TDD tradicionalmente prova que um comportamento novo funciona; esse ciclo específico provou, com um teste que falha se a verificação morta continuar sendo alcançável para dados da API primária, que a lógica realmente nunca executa, documentando a descoberta do SDD em vez de simplesmente apagar a linha e confiar na memória de quem viu o SDD.
O que isso muda na prática
O ganho não é documentação por documentação, é o timing. Registrar a decisão de "fallback só em exceção" depois de implementar não teria evitado nada; o código já teria sido escrito assumindo outra coisa. O valor do método está inteiramente em forçar essas descobertas para antes do primeiro commit, quando ainda custa uma frase no SDD em vez de um rollback em produção.
Vale notar também que os documentos não nascem completos na primeira tentativa. A primeira versão do ADR desse caso não tinha a decisão sobre disparo por exceção, nem a tabela de mapeamento de campos, ambas apareceram numa revisão posterior, depois que checar o código real contradisse a suposição inicial. Isso não é falha do processo; é o processo funcionando: cada camada expõe algo que a anterior não tinha deixado explícito, e o documento se atualiza antes de virar código, não depois.
Um efeito colateral prático: PR, descrição de QA e critérios de aceite passam a derivar do ADR/SDD/TDD, em vez de nascerem de suposição no momento da entrega. A rastreabilidade atividade → ADR → SDD → TDD → PR fecha um ciclo que normalmente fica quebrado, em geral só a atividade e o PR existem, com tudo o que aconteceu no meio guardado apenas na cabeça de quem implementou.
Um padrão, não uma regra fixa
O mesmo fluxo se repetiu, com o mesmo formato, em atividades de escopo bem menor, como uma correção de paridade entre dois fluxos de validação de um mesmo dado cadastral, onde o "achado" não foi um blocker de negócio, mas a descoberta de que uma exclusão de categoria de cliente na validação nova era inconsistente com o comportamento já validado no fluxo legado. O tamanho do problema mudou; a disciplina de gerar os três documentos antes de tocar em produção, não.
Isso sugere que o valor do método não está atado à complexidade da tarefa, está em nunca deixar a IA (ou o próprio dev, sob pressão de prazo) decidir implicitamente algo que deveria estar registrado explicitamente antes do código existir.
O que fica
No fundo, ADR, SDD e TDD não são burocracia pra justificar processo, são a forma de transformar "confiar na IA" em algo verificável, em vez de um ato de fé. Não é sobre desconfiar do agente; é sobre não terceirizar decisões que deveriam ser explícitas para um processo que, por natureza, prefere preencher lacunas em silêncio a admitir que não sabe.
A parte contraintuitiva é que isso não deixa o trabalho mais lento, deixa mais devagar só o primeiro passo, e mais rápido tudo o que vem depois, porque o retrabalho que normalmente aparece semanas depois, disfarçado de bug em produção, já foi resolvido numa frase de documento, antes de existir código pra reescrever. Plan first, then implement não é disciplina por disciplina. É a diferença entre a IA decidir por você sem avisar, e você decidir com a IA, de olhos abertos.
Top comments (0)