MÓDULO 1.4

✅ Código que Funciona: TDD + Diagnose

Red, green, refactor — com agente junto. Como usar TDD e o loop /diagnose para garantir que o código realmente funciona, em vez de só "parecer certo" pro agente.

9
Tópicos
45
Minutos
Intermediário
Nível
Prática
Tipo
1

🤔 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.

Evidência
teste rodando > "parece certo"
Critério
verde = pronto
Anti-loop
teste impede giro infinito
Contrato
teste é a especificação
2

🔄 /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.
Disciplina
uma etapa por vez
Ver falhar
teste tem que falhar antes
Mínimo
não otimize no green
Rede
refactor protegido por teste
3

🍰 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.

Demoável
cada slice mostrável
End-to-end
testa integração já
Feedback
descobre cedo o que quebra
Contexto
agente foca uma feature
4

🔬 /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.

1

Reproduce

Consiga ver o bug acontecendo, de forma confiável, no menor cenário possível. Sem reprodução, qualquer "fix" é chute.

2

Minimise

Reduza o caso até o esqueleto: menos arquivos, menos dados, menos passos. Quanto menor o caso, mais óbvia fica a causa.

3

Hypothesise

Liste hipóteses possíveis. Não vá direto na primeira que parece boa — escreva 3-5 e ordene por probabilidade.

4

Instrument

Adicione logs, prints, breakpoints que provem ou refutem cada hipótese. Não conserte ainda — só meça.

5

Fix

Agora sim, com a causa confirmada, escreva o conserto. Mínimo, focado, sem aproveitar pra "também arrumar isso aqui".

6

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.

Repro
ver acontecer = começo
Minimiza
corta tudo que não importa
Mede
prova antes de consertar
Trava
teste impede regressão
5

🎯 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.

Prova
repro mostra que existe
Medida
mesmo teste mede o fix
Anti-chute
sem repro = sem certeza
Acionável
repro vira teste depois
6

🛡️ 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
Trava
bug não volta sem alerta
Memória
documenta a causa raiz
Barato
repro já existe, vira teste
Cumulativo
suite vira blindagem
7

🔀 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. 1. /diagnose — reproduce: não consegue. Volta um passo: instrumenta produção (logs de transação id, timestamp, request id).
  2. 2. /diagnose — hypothesise: retry de cliente? race condition no worker? webhook duplicado do gateway?
  3. 3. /diagnose — instrument + minimise: logs mostram que dois workers pegam o mesmo job quando o cliente faz retry em <1s. Achou: race condition.
  4. 4. /tdd assume a partir daqui — red: escreve um teste que dispara dois workers simultâneos pro mesmo payload. Falha (duplica).
  5. 5. /tdd — green: implementa idempotency key na tabela de pagamentos. Teste passa.
  6. 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
Complementares
não escolhe um, usa os dois
Handoff
diagnose entrega ao tdd
Repro→Test
a ponte entre os dois
Definitivo
fix + teste = blindagem
8

🏋️ 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. 1. Escolha o bug. Cole no chat: descrição do problema, stack trace se tiver, contexto mínimo.
  2. 2. Rode /diagnose e siga as 6 etapas SEM pular.
  3. 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. 4. Quando chegar no fix, ANTES de aplicar, escreva o teste de regressão (etapa 6 do /diagnose).
  5. 5. Rode o teste — tem que falhar. Aplique o fix. Rode de novo — tem que passar.
  6. 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.

Concreto
bug real, não exemplo
Documentado
log de cada etapa
Verificável
teste prova o fix
Reutilizável
log vira referência futura

📚 Resumo do Módulo

Agente precisa de evidência objetiva — "parece certo" não vale; teste rodando vale.
/tdd: red → green → refactor — uma etapa por vez, ver o teste falhar antes de implementar.
Vertical slices > horizontais — feature inteira de ponta a ponta, demoável ao final de cada slice.
/diagnose: 6 etapas pra bugs difíceis — reproduce, minimise, hypothesise, instrument, fix, regression-test.
Reproduzir antes de consertar — sem repro, "fix" é chute; com repro, teste mede o conserto.
Bug consertado = teste de regressão — se não tem teste, o bug volta. Estatística, não pessimismo.
/diagnose + /tdd se completam — diagnose acha a causa, tdd implementa o fix definitivo.

Próximo Módulo:

1.5 — Próximos passos da trilha de Fundamentos