Corrigir o Mesmo Bug Duas Vezes É uma Falha de Processo
Todo time tem aquele defeito que não para de voltar. Um off-by-one na paginação. Uma verificação de nulo faltante no middleware de autenticação. Uma race condition no checkout que alguém “corrigiu” três sprints atrás.
Você não escreveu o mesmo bug três vezes. Você escreveu três bugs diferentes com a mesma causa. A correção tratou o sintoma. A causa permaneceu oculta.
As inspeções de Fagan foram construídas para encontrar defeitos antes que escapem. A maioria dos times para na fase de retrabalho. O autor corrige os problemas registrados, o moderador verifica as correções, e todos seguem em frente. Isso é um erro. A fase de acompanhamento é onde a análise causal pertence. Pular ela é agendar a próxima inspeção para os mesmos tipos de defeitos.
O Que a Análise Causal Realmente Significa
Análise causal não é análise de causa raiz. Análise de causa raiz pergunta “qual linha de código falhou e por quê.” Análise causal pergunta “o que em nosso processo permitiu que esta categoria de defeito existisse.”
Uma causa raiz de uma exceção de ponteiro nulo é “esquecemos de verificar o nulo.” O achado da análise causal é “nossa checklist de revisão não inclui null safety, e nossas regras de linting permitem dereferências não verificadas.” Um corrige o bug. O outro conserta a fábrica.
Em uma inspeção de Fagan, a análise causal acontece após o retrabalho. O moderador agrupa os defeitos por categoria e conduz uma sessão curta para identificar causas em nível de processo. O resultado não são mudanças de código. São mudanças de processo: checklists atualizadas, novas regras de lint, lacunas de treinamento ou critérios de entrada modificados.
A Mecânica: Como Executar
Comece com o log de inspeção. Cada defeito já deve ter quatro campos: localização, severidade, tipo e descrição. Adicione um quinto durante a análise causal: causa de processo.
O moderador agrupa os defeitos por tipo. Se três de doze defeitos são erros de condição de limite, isso é um padrão. Se dois são erros de uso incorreto de API do mesmo module, isso também é um padrão. Padrões são o sinal. Defeitos individuais são ruído.
Para cada padrão, faça três perguntas:
- Poderíamos ter prevenido esta categoria antes da inspeção?
- Por que nossos mecanismos de prevenção existentes perderam isso?
- Qual é a mudança mais barata que preveniria esta categoria da próxima vez?
A terceira pergunta é onde a maioria dos times erra. Eles sugerem reescrever a arquitetura. Isso é um desejo irrealizável. O objetivo é a menor mudança de processo que elimina a categoria.
Um padrão de condição de limite pode significar adicionar “verificações off-by-one e de limites” à checklist de revisão. Um padrão de uso incorreto de API pode significar adicionar uma regra de análise estática. Um padrão de tratamento de erros faltante pode significar atualizar a definição de pronto para exigir testes de caminho de erro.
Um Rastreador de Análise Causal Funcional
Aqui está um script Python que pega um log de inspeção, agrupa defeitos por tipo, e solicita causas em nível de processo.
#!/usr/bin/env python3
"""
Run causal analysis on a Fagan inspection log.
Reads a JSON log, groups defects by type, and emits a causal analysis report.
"""
import json
import argparse
from collections import defaultdict
from dataclasses import dataclass, field
from typing import List, Dict
@dataclass
class Defect:
location: str
severity: str
defect_type: str
description: str
@dataclass
class CausalPattern:
defect_type: str
count: int
locations: List[str] = field(default_factory=list)
process_cause: str = ""
proposed_fix: str = ""
def load_log(path: str) -> List[Defect]:
with open(path, "r") as f:
raw = json.load(f)
return [Defect(**item) for item in raw]
def analyze_patterns(defects: List[Defect]) -> List[CausalPattern]:
groups: Dict[str, List[Defect]] = defaultdict(list)
for d in defects:
groups[d.defect_type].append(d)
patterns = []
for dtype, items in groups.items():
patterns.append(CausalPattern(
defect_type=dtype,
count=len(items),
locations=[d.location for d in items],
))
return sorted(patterns, key=lambda p: p.count, reverse=True)
def prompt_causal_input(patterns: List[CausalPattern]) -> List[CausalPattern]:
print("=== CAUSAL ANALYSIS SESSION ===")
print("For each pattern, identify the process cause and the cheapest fix.\n")
for p in patterns:
print(f"Pattern: {p.defect_type} ({p.count} occurrence(s))")
print(f"Locations: {', '.join(p.locations)}")
p.process_cause = input("Process cause: ").strip()
p.proposed_fix = input("Cheapest prevention fix: ").strip()
print()
return patterns
def emit_report(patterns: List[CausalPattern], output_path: str):
report = {
"summary": {
"total_patterns": len(patterns),
"total_defects": sum(p.count for p in patterns),
},
"patterns": [
{
"type": p.defect_type,
"count": p.count,
"locations": p.locations,
"process_cause": p.process_cause,
"proposed_fix": p.proposed_fix,
}
for p in patterns
],
}
with open(output_path, "w") as f:
json.dump(report, f, indent=2)
print(f"Report written to {output_path}")
def main():
parser = argparse.ArgumentParser(description="Causal analysis for Fagan inspections")
parser.add_argument("log", help="Path to inspection log JSON")
parser.add_argument("--output", default="causal_report.json", help="Output report path")
args = parser.parse_args()
defects = load_log(args.log)
patterns = analyze_patterns(defects)
if not patterns:
print("No defects found. Nothing to analyze.")
return
patterns = prompt_causal_input(patterns)
emit_report(patterns, args.output)
if __name__ == "__main__":
main()
Salve um log de inspeção como inspection_log.json:
[
{"location": "src/auth.py:42", "severity": "major", "defect_type": "null-safety", "description": "Missing null check on user object"},
{"location": "src/orders.py:88", "severity": "minor", "defect_type": "boundary", "description": "Off-by-one in pagination limit"},
{"location": "src/auth.py:67", "severity": "major", "defect_type": "null-safety", "description": "Unchecked token decode result"}
]
Execute python causal_analysis.py inspection_log.json e o script o guiará para identificar causas de processo. O relatório que ele produz se torna a entrada para sua próxima retrospectiva ou ciclo de melhoria de processos.
Por Que a Maioria dos Times Pula Isso
A análise causal adiciona tempo a um processo já caro. Uma inspeção padrão de Fagan para 250 linhas custa de 8 a 12 horas-pessoa. A análise causal adiciona mais 30 a 60 minutos.
Esse tempo extra parece desperdício quando você tem um backlog. Mas se a análise causal prevenir mesmo uma única recorrência de uma categoria de defeito, ela se paga da próxima vez que essa categoria não aparecer.
O problema mais difícil é a honestidade. A análise causal frequentemente revela que o defeito existia porque o time pulou uma etapa. O código não foi testado antes da inspeção. O revisor não usou a checklist. A própria checklist está incompleta.
Esses achados podem ser desconfortáveis. Um time que trata a análise causal como atribuição de culpa deixará de obter respostas honestas. O moderador deve enquadrá-la como melhoria de processo, não como teatro de responsabilização.
A Verdadeira Troca: Velocidade vs. Aprendizado
Você tem duas opções após uma inspeção de Fagan. Fechar o ciclo verificando as correções e seguindo em frente. Ou fechar o ciclo verificando as correções e aprendendo por que os defeitos existiam.
A primeira opção é mais rápida hoje. A segunda opção é mais rápida nos próximos seis meses.
Os times que obtêm mais valor das inspeções de Fagan tratam o log de inspeção como um conjunto de dados. Padrões de defeitos são feedback. Ignorá-los é como executar uma test suite e nunca olhar para as falhas.
Nem toda categoria de defeito merece análise causal. Um erro de digitação único não precisa de mudança de processo. Mas se uma categoria aparece em duas inspeções consecutivas, você tem um problema de processo se passando por um problema de código.
Comece com o module de Maior Impacto
Você não precisa executar análise causal em toda inspeção. Escolha o module que produz a maioria dos incidents de produção ou a maioria dos defeitos escapados. Execute uma inspeção completa de Fagan, colete o log, e gaste trinta minutos em análise causal.
A primeira vez que você fizer isso, as correções propostas serão óbvias. Atualize a checklist. Adicione uma regra de lint. Escreva uma breve nota do time sobre o padrão. Na terceira inspeção, você deve ver menos defeitos nas categorias que já analisou.
Se as contagens não estiverem caindo, suas correções propostas são vagas demais. “Seja mais cuidadoso” não é uma mudança de processo. “Execute o null safety linter no CI” é.
Acompanhe as categorias de defeitos por inspeção ao longo do tempo. Se a análise causal estiver fazendo seu trabalho, as categorias recorrentes desaparecem. Se não desaparecerem, você está identificando as causas incorretamente ou falhando em implementar as correções.
FAQ
O que é análise causal em uma inspeção de Fagan?
Análise causal é uma etapa pós-inspeção onde o time examina os defeitos encontrados, os agrupa por categoria e identifica causas em nível de processo. O objetivo é prevenir recorrências alterando checklists, ferramentas ou práticas, não apenas corrigindo os defeitos individuais.
Como a análise causal difere da análise de causa raiz?
A análise de causa raiz identifica a razão específica pela qual um único defeito ocorreu, como uma verificação de nulo faltante. A análise causal identifica por que o processo permitiu que toda aquela categoria de defeito fosse escrita, como um item de checklist faltante ou uma regra de lint não aplicada.
Quando devo executar a análise causal?
Execute-a após as fases de retrabalho e acompanhamento de uma inspeção de Fagan, enquanto os defeitos ainda estão frescos. Foque em padrões que aparecem em múltiplos defeitos ou que já apareceram em inspeções anteriores. Defeitos únicos raramente justificam mudanças de processo.
Como sei se a análise causal está funcionando?
Acompanhe as categorias de defeitos através das inspeções ao longo do tempo. Se sua análise causal for efetiva, a frequência de categorias recorrentes deve cair. Se as mesmas categorias continuarem aparecendo, suas correções propostas estão incorretas ou não estão sendo implementadas.
Execute na Seu Próximo Log de Inspeção
Encontre seu último log de inspeção. Agrupe os defeitos por tipo. Para a categoria principal, pergunte qual seria a mudança de processo mais barata para preveni-la da próxima vez.
Escreva essa mudança. Atribua um responsável. Verifique-a na próxima inspeção. Isso é análise causal. Todo o resto é apenas correção de bugs.