MÓDULO 1.6

🐛 Debugging sistemático

Pare de atirar no escuro. O processo de 4 fases que transforma debugging de adivinhação em investigação.

📋6 tópicos ⏱️~30 min 🎯Intermediário 📖Teoria + Prática
1

🔬 As 4 fases de causa raiz

Debugging sistemático não é mais rápido que debugging aleatório em cada bug individual — mas é dramaticamente mais rápido em média, porque elimina as tentativas que não funcionam antes de tentar. O processo de 4 fases aplica o método científico ao diagnóstico de bugs.

1

Observar o sintoma

Documentar exatamente o que está acontecendo: mensagem de erro completa, stack trace, condições de reprodução, frequência.

2

Formular hipóteses

Listar 3-5 possíveis causas, em ordem de probabilidade. Não começar a testar ainda — primeiro pensar.

3

Testar hipóteses

Para cada hipótese, criar um teste mínimo que a confirme ou descarte. Testar uma hipótese por vez, registrando o resultado.

4

Identificar causa raiz

A hipótese confirmada é a causa raiz. Corrigir especificamente essa causa, não o sintoma. Verificar com evidência.

2

⏳ Condition-based waiting: não dormir, esperar condição

Um dos bugs mais insidiosos em sistemas assíncronos é o race condition causado por sleep fixo. O agente usa sleep porque parece simples, mas sleep não garante que a condição que você está esperando realmente se cumpriu — apenas que passou tempo suficiente para que provavelmente tenha se cumprido.

Ruim: sleep fixo

await startServer();
await sleep(2000); // esperamos 2s
const res = await fetch('/health');
// falha se servidor demorou 2.1s

Bom: condition-based

await startServer();
await waitUntil(
  () => fetch('/health').then(r => r.ok),
  { timeout: 10000, interval: 100 }
);
// só continua quando o servidor responder

Instrua o agente explicitamente

Agentes usam sleep por padrão porque está no treinamento deles. Instrua: "Nunca use sleep fixo para sincronização. Use polling de condição com timeout e intervalo configurável."

3

✅ verification-before-completion: evidência real

Um dos problemas mais frustrantes com agentes é que eles frequentemente declaram "bug corrigido" antes de verificar se a correção realmente funciona. A skill debugging instrui o agente a exigir evidência verificável antes de qualquer declaração de sucesso.

O que conta como evidência real

  • Output de npm test mostrando que os testes passam
  • Log do servidor mostrando a requisição sendo processada corretamente
  • Saída do curl/fetch com o status code e body esperados
  • Screenshot do comportamento visual funcionando

O que NÃO é evidência

  • • "Fiz a correção, deve funcionar agora"
  • • "A lógica parece correta"
  • • "O código compila sem erros"
  • • "Não vejo mais o erro no código"
4

🧵 Root cause tracing: técnicas avançadas

Para bugs em sistemas complexos, as 4 fases básicas não são suficientes. Técnicas avançadas de rastreamento permitem encontrar origens não intuitivas, como bugs que surgem da interação de dois sistemas corretos individualmente.

Git bisect

Quando o bug existe na versão atual mas não em um commit anterior, git bisect faz busca binária automática no histórico de commits para encontrar exatamente quando o bug foi introduzido.

Reprodução mínima

Criar a versão mais simples possível do sistema que ainda reproduz o bug. Cada componente removido que mantém o bug é uma hipótese eliminada.

Log estruturado

Adicionar logs com contexto (timestamp, request ID, user ID, estado relevante) em pontos estratégicos para criar uma trilha rastreável do fluxo de execução.

Isolamento de variável

Mudar uma variável de cada vez (input, ambiente, configuração, dados) para identificar qual combinação específica causa o bug.

5

🛡️ Defense-in-depth: prevenindo regressão

Corrigir um bug sem adicionar proteção contra regressão é incompleto. A regressão ocorre quando uma correção é desfeita por uma mudança futura — seja por outro agente, por refatoração ou por nova feature. Defense-in-depth cria múltiplas camadas que protegem a correção.

As camadas de proteção

  • 1.Teste de regressão — um teste que falha se o bug voltar
  • 2.Assertion defensiva — validação no código que lança erro se a condição inválida ocorrer
  • 3.Comentário explicativo — "Este código existe porque X causa Y. Não remover sem testar Z."
  • 4.Validação de input — rejeitar na entrada o que causou o bug, não apenas no ponto de falha

Bugs que voltam são os mais frustrantes de debugar porque parecem resolvidos. Defense-in-depth garante que se o bug tentar voltar, ele seja detectado antes de chegar à produção.

6

🛠️ Prática: diagnosticar um bug sem atirar no escuro

Se o código do módulo anterior tem bugs, use-os como exercício. Se não tem, introduza um bug propositalmente para praticar. O objetivo não é a velocidade — é executar o processo de 4 fases sem pular etapas.

Roteiro do exercício

  1. 1.Documente o sintoma completamente: erro, stack trace, condições de reprodução
  2. 2.Liste 3 hipóteses de causa raiz antes de executar qualquer código
  3. 3.Teste cada hipótese com um mini-teste isolado
  4. 4.Identifique a causa raiz e corrija especificamente ela
  5. 5.Colete evidência real de que a correção funcionou
  6. 6.Adicione teste de regressão e protection layer

Critério de conclusão

Bug corrigido com evidência real, teste de regressão adicionado, e um comentário explicando por que a correção existe. O processo foi seguido sem pular etapas.

Resumo do Módulo 1.6

Domino o processo de 4 fases: sintoma → hipótese → teste → causa raiz
Substituo sleep fixo por condition-based waiting
Exijo evidência real antes de declarar bug corrigido
Conheço técnicas avançadas: bisect, reprodução mínima, log estruturado
Adiciono defense-in-depth para prevenir regressões
Completei o exercício de debugging sem atirar no escuro
Próximo módulo: 1.7 — Revisão de código com agentes