🤔 Por que TDD com agente
Agente é um péssimo verificador do próprio trabalho. Ele lê o código que acabou de gerar, vê que "parece certo", e declara vitória. Sem teste rodando, "parece certo" é só uma opinião — e a opinião do agente está enviesada pelo código que ele mesmo escreveu.
TDD resolve isso virando o jogo: o teste vem primeiro, falha primeiro, e só depois o código é escrito pra fazer o teste passar. O agente não tem como se enganar — ou o teste fica verde, ou não fica.
🎯 O princípio central
Agentes precisam de sinais objetivos de progresso. Sem isso, eles giram em círculo, "consertando" coisas que já funcionavam e quebrando o que estava bom.
- • Teste vermelho = trabalho a fazer
- • Teste verde = pode parar
- • Sem teste = sem critério de parada
💡 Dica prática
Antes de mandar o agente implementar qualquer coisa nova, pergunte: "qual o teste que prova que isso funciona?". Se você não consegue descrever o teste, o agente também não vai conseguir verificar o resultado.
🔄 /tdd: o loop red-green-refactor
O comando /tdd coloca o agente num loop disciplinado de três etapas. Cada etapa tem um objetivo único — não pular, não combinar, não otimizar antes da hora.
Red — escreve teste que falha
Tempo: 2-5 min
Defina o comportamento esperado em forma de teste. Rode o teste e veja-o falhar. Se ele passar de cara, ou o teste está errado, ou a feature já existe. Esse "ver falhar" é a prova de que o teste mede algo de verdade.
Green — código mínimo pra passar
Tempo: 5-15 min
Escreva a menor quantidade de código possível pra deixar o teste verde. Hardcode é permitido. Feio é permitido. Lento é permitido. O objetivo é só ver o verde — vai melhorar na próxima etapa.
Refactor — limpa mantendo verde
Tempo: 5-10 min
Agora sim: remova duplicação, dê nomes melhores, extraia funções. A cada mudança, rode os testes. Se virar vermelho, desfaça. O teste é a rede de segurança que torna o refactor seguro.
💻 Diálogo real de /tdd
# você /tdd implementa função slugify(texto) — minúsculas, espaço vira "-", remove acentos, remove caracteres especiais # agente 🔴 Red: escrevi 4 testes em test_slugify.py - "Olá Mundo" → "ola-mundo" - " Espaço extra " → "espaco-extra" - "Já!!" → "ja" - "" → "" rodando pytest... 4 failed (módulo slugify não existe) ✓ esperado 🟢 Green: criando slugify.py com unicodedata + re.sub rodando pytest... 4 passed ✓ ♻️ Refactor: extraindo etapas em funções nomeadas _strip_accents(s), _to_kebab(s) rodando pytest após cada mudança... 4 passed ✓ pronto.
🍰 Vertical slices: uma fatia completa por vez
Quando uma feature toca várias camadas (UI, API, DB), tem dois jeitos de atacar: horizontalmente (faz toda a DB, depois toda a API, depois toda a UI) ou verticalmente (uma feature de ponta a ponta, depois a próxima). Com agente, vertical sempre ganha.
Vertical slice = você consegue rodar e ver funcionando ao final de cada slice. Horizontal = você só descobre que tudo está errado quando junta tudo no final.
✓ Vertical (FAZER)
- ✓ Feature "criar comentário": migration + endpoint + form, tudo numa slice
- ✓ Teste end-to-end roda ao final de cada slice
- ✓ Demoável: dá pra mostrar funcionando depois de cada slice
- ✓ Feedback rápido: integração testada a cada feature
✗ Horizontal (EVITAR)
- ✗ "Primeiro faço todas as tabelas, depois todos os endpoints"
- ✗ Nada roda end-to-end até o final do projeto
- ✗ Big bang integration: tudo quebra junto no fim
- ✗ Agente perde contexto entre camadas e fica inconsistente
📐 Vertical vs Horizontal — diagrama
VERTICAL (recomendado) HORIZONTAL (evitar) ┌──────┬──────┬──────┐ ┌──────────────────┐ │ UI │ UI │ UI │ │ UI │ ← slice 3 ├──────┼──────┼──────┤ ├──────────────────┤ │ API │ API │ API │ │ API │ ← slice 2 ├──────┼──────┼──────┤ ├──────────────────┤ │ DB │ DB │ DB │ │ DB │ ← slice 1 └──────┴──────┴──────┘ └──────────────────┘ feat1 feat2 feat3 tudo de uma vez cada slice roda só roda no fim de tudo end-to-end (e geralmente quebra)
💡 Regra prática
Se o agente termina uma slice e você não consegue abrir o app e usar aquela feature, a slice não acabou. Vertical = demoável ao final.
🔬 /diagnose: para bugs difíceis
TDD resolve bem o trabalho novo. Mas bugs em código existente — especialmente os intermitentes, os que só acontecem em produção, os que ninguém entende — pedem outra abordagem. O /diagnose é um loop de seis etapas pensado pra isso.
Reproduce
Consiga ver o bug acontecendo, de forma confiável, no menor cenário possível. Sem reprodução, qualquer "fix" é chute.
Minimise
Reduza o caso até o esqueleto: menos arquivos, menos dados, menos passos. Quanto menor o caso, mais óbvia fica a causa.
Hypothesise
Liste hipóteses possíveis. Não vá direto na primeira que parece boa — escreva 3-5 e ordene por probabilidade.
Instrument
Adicione logs, prints, breakpoints que provem ou refutem cada hipótese. Não conserte ainda — só meça.
Fix
Agora sim, com a causa confirmada, escreva o conserto. Mínimo, focado, sem aproveitar pra "também arrumar isso aqui".
Regression-test
Transforme a reprodução do passo 1 em teste automatizado. Se o bug voltar um dia, o CI grita antes do usuário.
📊 Por que esse loop em vez de "ir consertando"
Bug difícil é difícil porque sua intuição já falhou pelo menos uma vez (senão você já tinha consertado). Pular pra "fix" antes de "reproduce + instrument" é apostar que sua intuição vai acertar dessa vez — geralmente não acerta, e o agente reforça o erro escrevendo código que parece resolver mas não resolve.
🎯 Reproduzir antes de consertar
O erro mais caro em debug é pular a etapa de reprodução. O agente lê o stack trace, vê um nome familiar, e sai escrevendo um fix. Às vezes funciona — mas quando não funciona, ninguém percebe, porque ninguém montou o cenário pra verificar.
✓ Reproduzir primeiro
- ✓ "Antes de tocar no código, vou escrever um teste mínimo que falha igual ao bug"
- ✓ Confirmar que o teste falha pelo motivo certo (mesmo erro, mesma stack)
- ✓ Só então atacar a causa — e medir o resultado pelo mesmo teste
- ✓ Quando o teste passa, você TEM PROVA de que consertou
✗ Anti-padrão: pular pro fix
- ✗ "Ah, sei o que é, vou consertar" — sem reproduzir
- ✗ Agente que lê o erro e já sai mexendo em 5 arquivos
- ✗ "Acho que era isso" — sem forma de provar
- ✗ Bug volta uma semana depois porque o "fix" não conserta o caso real
⚠️ Sinal de alerta
Se o agente diz "vou ajustar isso aqui" e parte editando código sem ter mostrado uma reprodução, interrompa. Peça primeiro: "como você reproduz o bug?". Se não souber responder, ele está chutando.
🛡️ Regression tests: todo bug consertado vira teste
Bug que voltou é o mais frustrante de todos — porque ele já foi consertado uma vez. A regra é simples: todo bug consertado vira um teste de regressão. Se você não tem teste pro caso, ele vai voltar.
O melhor momento de escrever esse teste é exatamente quando você acabou de consertar o bug — o cenário está fresco, você sabe exatamente o que falhava, e a reprodução já existe (você usou ela no /diagnose).
💻 Exemplo: teste de regressão
# Bug reportado em produção: "comentário com emoji 🎉 quebrava a API" # Causa encontrada via /diagnose: encoding latin-1 num middleware antigo # Fix aplicado: trocar pra utf-8 # Teste de regressão (commit junto com o fix): def test_comentario_com_emoji_nao_quebra_api(): """Regression: bug #482 — emoji no body causava UnicodeDecodeError.""" payload = {"texto": "Show de bola 🎉🚀"} response = client.post("/api/comentarios", json=payload) assert response.status_code == 201 assert response.json()["texto"] == "Show de bola 🎉🚀" # Anote no docstring: número do bug, causa raiz. Quando o teste falhar # de novo em 6 meses, quem ler vai entender o porquê de existir.
💡 Se não tem teste, o bug volta
Não é pessimismo, é estatística. Refactors futuros, mudanças em dependência, alguém limpando "código velho que ninguém usa" — qualquer um desses traz o bug de volta. O teste é o lembrete automático: "esse caso aqui precisa continuar funcionando".
📊 Padrão de docstring
- • Prefixo:
Regression:deixa explícito que o teste existe por causa de um bug - • Referência: número do issue/ticket/PR pra rastrear contexto
- • Causa raiz: uma linha — pra quem mexer no teste depois entender
- • NÃO apagar: teste de regressão é patrimônio do projeto
🔀 Combinando /tdd + /diagnose
Os dois loops não competem — colaboram. /tdd é pra trabalho novo onde você sabe o que quer. /diagnose é pra entender por que algo existente não está fazendo o que devia. Bugs complexos quase sempre passam por ambos.
🎬 Fluxo: bug intermitente em produção
Imagine: relatório de bug "às vezes o pagamento duplica". Você não consegue reproduzir local. O que fazer?
- 1. /diagnose — reproduce: não consegue. Volta um passo: instrumenta produção (logs de transação id, timestamp, request id).
- 2. /diagnose — hypothesise: retry de cliente? race condition no worker? webhook duplicado do gateway?
- 3. /diagnose — instrument + minimise: logs mostram que dois workers pegam o mesmo job quando o cliente faz retry em <1s. Achou: race condition.
- 4. /tdd assume a partir daqui — red: escreve um teste que dispara dois workers simultâneos pro mesmo payload. Falha (duplica).
- 5. /tdd — green: implementa idempotency key na tabela de pagamentos. Teste passa.
- 6. /tdd — refactor + regression: limpa o código, mantém o teste como regressão permanente. Bug não volta.
🧭 Quando alternar
- • Sabe o que quer construir? → /tdd direto
- • Tem bug e sabe reproduzir? → /diagnose até causa raiz, depois /tdd pro fix
- • Tem bug e NÃO sabe reproduzir? → /diagnose começa com instrumentação remota antes de qualquer fix
- • Refactor com testes existentes? → fica no green-refactor do /tdd
- • Refactor SEM testes? → /tdd primeiro: cobre o comportamento atual com testes; só depois refactor
🏋️ Exercício prático
Pegue um bug aberto no seu projeto — qualquer um, de preferência um que você tem evitado porque "é estranho". Rode /diagnose e anote cada etapa num documento. No final, você vai ter o bug resolvido E um material de revisão pra entender como agentes lidam com bugs.
📋 Roteiro do exercício
- 1. Escolha o bug. Cole no chat: descrição do problema, stack trace se tiver, contexto mínimo.
- 2. Rode
/diagnosee siga as 6 etapas SEM pular. - 3. A cada etapa, anote em um
diagnose-log.md: o que o agente fez, o que descobriu, o que ainda não estava claro. - 4. Quando chegar no fix, ANTES de aplicar, escreva o teste de regressão (etapa 6 do /diagnose).
- 5. Rode o teste — tem que falhar. Aplique o fix. Rode de novo — tem que passar.
- 6. Commit do fix + teste juntos, com mensagem referenciando a causa raiz.
💡 Critério de sucesso
Não é "o bug foi resolvido". É "eu sei provar que o bug foi resolvido, e tenho um teste que vai gritar se ele voltar". Se você não consegue marcar essas duas caixas, refaça o exercício.
📚 Resumo do Módulo
Próximo Módulo:
1.5 — Próximos passos da trilha de Fundamentos