A maioria dos checklists de revisão são placebos
Se sua equipe tem um checklist para code review, há uma boa chance de que ele viva em uma página wiki que ninguém abre. Ele provavelmente diz coisas como „check for off-by-one errors” e „verify error handling.” Essas são afirmações verdadeiras. Elas também são vagas demais para mudar o comportamento.
Um checklist que diz para você „check for bugs” não é um checklist. É um lembrete de que bugs existem.
O checklist em uma Fagan inspection serve a um propósito diferente. Não é uma lista de coisas com que se preocupar. É uma ferramenta estruturada que direciona a atenção do reviewer para categorias específicas de defeitos que foram observadas no codebase real sendo revisado. Ele é construído a partir de dados, adaptado ao tipo de artefato e usado durante a preparação individual obrigatória. Quando usado corretamente, é uma das principais razões pelas quais uma inspection estruturada captura de três a quatro vezes mais defeitos do que uma revisão informal.
O que torna um checklist „estruturado”
A palavra „estruturado” importa aqui. Um checklist estruturado não é um checklist mais longo. É um checklist com propriedades de design específicas.
Primeiro, ele é derivado de dados de defeitos escapados. Os itens vêm de bugs que realmente chegaram à produção, não de um documento genérico de boas práticas. Se seus últimos três incidents envolveram race conditions em async cleanup, essa categoria ganha seu próprio item no checklist. Se null dereferences não foram um problema em dois anos, esse item é removido.
Segundo, ele é limitado ao tipo de artefato sendo revisado. As Fagan inspections originalmente usavam checklists diferentes para documentos de requisitos, documentos de design, source code e planos de teste. Cada artefato tem categorias de defeitos diferentes. Um checklist de documento de design pergunta sobre consistência de interfaces e acoplamento. Um checklist de source code pergunta sobre boundary conditions e limpeza de recursos. Misturá-los dilui ambos.
Terceiro, ele é usado durante a preparação individual, não durante a reunião. Cada inspector lê o material sozinho, com o checklist, antes do grupo sequer se reunir. O checklist molda o que cada pessoa vê quando lê o código de forma isolada. Não é um documento de referência compartilhado. É uma lente pessoal.
Quarto, ele é curto o suficiente para ser usável. Um checklist com quarenta itens é um catálogo, não uma ferramenta. Fagan recomendava aproximadamente dez a quinze itens por tipo de artefato. A restrição força a priorização. Você mantém as categorias que importam e descarta o ruído.
Como construir um a partir dos seus próprios dados
A melhor maneira de construir um checklist estruturado é observar o que já deu errado. Aqui está um processo prático.
Comece com seu incident tracker, sua bug database ou suas notas de postmortem. Extraia os últimos vinte defeitos que escaparam da revisão e chegaram à produção ou testes. Categorize cada um por root cause, não por sintoma. „Page crashed” é um sintoma. „Missing null check after external API response” é uma root cause.
Agrupe as root causes em categorias. Você provavelmente descobrirá que 80 por cento dos seus defeitos escapados caem em três a cinco categorias. Esses são seus itens de checklist.
Para cada categoria, escreva uma pergunta-gatilho específica, não um lembrete vago. „Check for nulls” é vago. „For every function that calls an external API, verify the response is validated before use” é uma pergunta-gatilho. Ela diz exatamente ao reviewer o que procurar e onde procurar.
Aqui está um exemplo concreto. Suponha que sua equipe faça deploy de serviços Python e você analisou os últimos vinte issues de produção. Você encontra esta distribuição:
- 6 issues: tratamento de erro ausente em chamadas externas
- 5 issues: limites de transaction de banco de dados incorretos
- 4 issues: off-by-one na lógica de paginação
- 3 issues: race conditions em dados em cache
- 2 issues: registro de dados sensíveis
Seu checklist deve ter cinco itens, um para cada categoria. Cada item deve ser uma pergunta-gatilho vinculada aos seus patterns específicos.
#!/usr/bin/env python3
"""Build a structured review checklist from escaped defect data."""
from collections import Counter
from dataclasses import dataclass
from typing import List
@dataclass
class EscapedDefect:
id: str
root_cause: str
trigger_question: str
def build_checklist(defects: List[EscapedDefect], max_items: int = 10) -> List[str]:
"""Build a Fagan-style checklist from escaped defect data.
Groups by root cause, sorts by frequency, and returns trigger
questions for the top categories.
"""
counts = Counter(d.root_cause for d in defects)
top_causes = counts.most_common(max_items)
# Map root causes back to their most representative trigger question
cause_to_question = {}
for d in defects:
if d.root_cause not in cause_to_question:
cause_to_question[d.root_cause] = d.trigger_question
checklist = []
for cause, count in top_causes:
question = cause_to_question[cause]
checklist.append(f"[{count}x] {question}")
return checklist
# Example: escaped defects from a Python web service
DEFECTS = [
EscapedDefect("BUG-101", "missing-external-error-handling",
"For every external API call, is the response validated before use?"),
EscapedDefect("BUG-102", "missing-external-error-handling",
"For every external API call, is the response validated before use?"),
EscapedDefect("BUG-103", "missing-external-error-handling",
"For every external API call, is the response validated before use?"),
EscapedDefect("BUG-104", "transaction-boundary-error",
"Does every database write have the correct transaction scope?"),
EscapedDefect("BUG-105", "transaction-boundary-error",
"Does every database write have the correct transaction scope?"),
EscapedDefect("BUG-106", "pagination-off-by-one",
"For every pagination query, are the limit and offset tested at boundaries?"),
EscapedDefect("BUG-107", "cache-race-condition",
"For every cached value, is there a clear invalidation or TTL strategy?"),
EscapedDefect("BUG-108", "sensitive-data-in-logs",
"Does any log statement include user data, tokens, or PII?"),
]
if __name__ == "__main__":
checklist = build_checklist(DEFECTS, max_items=5)
print("Structured Review Checklist")
print("=" * 40)
for item in checklist:
print(f" [ ] {item}")
Execute este script com seus próprios dados de bugs e você terá um checklist vinculado aos seus failure modes reais, não aos de outra pessoa.
Como a sessão de revisão realmente se parece
Com o checklist em mãos, a sessão de revisão tem um ritmo específico.
Durante a preparação individual, cada inspector lê o material sozinho a aproximadamente 100 a 150 linhas por hora. Eles usam o checklist como uma lente, não como um roteiro. Eles não trabalham o checklist item por item em ordem. Eles leem o código naturalmente, e quando encontram um pattern que corresponde a uma categoria do checklist, param e o examinam cuidadosamente.
O checklist não substitui a leitura. É um pattern matcher para coisas que seu cérebro provavelmente pula.
No inspection meeting, o reader parafraseia o código em voz alta. Quando um inspector detecta um defeito potencial, o levanta imediatamente. As categorias do checklist fornecem um vocabulário compartilhado. Em vez de dizer „isso parece errado”, um inspector pode dizer „isso parece um erro de limite de transaction, categoria dois.” O moderator registra. O author escuta. Ninguém propõe uma correção.
O checklist não é consultado durante a reunião. Nesse ponto, os inspectors já o internalizaram. A reunião é para verificar cruzadamente o que cada pessoa encontrou de forma isolada.
Por que checklists genéricos falham
A maioria das equipes que tentam checklists desistem porque usam o tipo errado.
Um checklist genérico copiado de um blog post não tem peso emocional. Ele pede ao reviewer para procurar coisas que talvez nunca tenham acontecido em seu codebase. O reviewer o percorre rapidamente, não vê nada familiar e volta a ler o diff como sempre fez.
Um checklist estruturado construído a partir de defeitos escapados é diferente. Cada item representa um incident real que custou tempo real. O reviewer conhece esses bugs porque viu os postmortems. O checklist conecta o comportamento de revisão a falhas específicas e memoráveis.
O outro erro comum é tratar o checklist como uma ferramenta de reunião em vez de uma ferramenta de preparação. Se você tirar o checklist durante a reunião de revisão, ele se torna um roteiro para uma varredura em grupo. Todos leem o mesmo item, olham para o mesmo código e convergem para as mesmas observações óbvias. O poder do checklist está na preparação individual, onde seis pessoas aplicam as mesmas categorias de forma independente e encontram coisas diferentes.
A carga de manutenção que a maioria das equipes ignora
Um checklist não é um monumento. É um documento vivo que apodrece mais rápido que o código.
Se você adicionar um item ao checklist toda vez que algo dá errado, mas nunca remover um, terá quarenta itens em seis meses. Nesse ponto, os reviewers começam a tratá-lo como papel de parede.
Fagan recomendou revisar o checklist em si após cada poucas inspections. Remova itens que não tenham gerado uma descoberta nas últimas cinco revisões. Adicione itens apenas para novas categorias de defeitos que escaparam para a produção. Mantenha o total abaixo de quinze. Se você não conseguir mantê-lo curto, não está priorizando.
Aqui está um script leve para podar um checklist com base em dados históricos de descobertas:
#!/usr/bin/env python3
"""Prune a review checklist: remove items that no longer trigger findings."""
from dataclasses import dataclass
from typing import List, Dict
@dataclass
class ChecklistItem:
category: str
trigger_question: str
findings_last_5_reviews: int
def prune_checklist(items: List[ChecklistItem], min_findings: int = 1) -> List[ChecklistItem]:
"""Remove checklist items that have not produced findings recently.
A Fagan-style checklist should be short enough to be usable.
Items that sit idle waste attention.
"""
kept = [item for item in items if item.findings_last_5_reviews >= min_findings]
removed = [item for item in items if item.findings_last_5_reviews < min_findings]
print(f"Kept {len(kept)} items, removed {len(removed)} items")
for item in removed:
print(f" REMOVED: [{item.category}] {item.trigger_question}")
return kept
# Example: current checklist with finding counts from the last 5 reviews
CURRENT_CHECKLIST = [
ChecklistItem("missing-external-error-handling",
"For every external API call, is the response validated before use?", 4),
ChecklistItem("transaction-boundary-error",
"Does every database write have the correct transaction scope?", 3),
ChecklistItem("pagination-off-by-one",
"For every pagination query, are the limit and offset tested at boundaries?", 0),
ChecklistItem("cache-race-condition",
"For every cached value, is there a clear invalidation or TTL strategy?", 1),
ChecklistItem("sensitive-data-in-logs",
"Does any log statement include user data, tokens, or PII?", 0),
]
if __name__ == "__main__":
pruned = prune_checklist(CURRENT_CHECKLIST, min_findings=1)
print("\nActive checklist:")
for item in pruned:
print(f" [ ] [{item.findings_last_5_reviews}x] {item.trigger_question}")
Agende esta revisão trimestralmente. Um checklist obsoleto é pior do que nenhum porque treina os reviewers a ignorar a ferramenta.
O trade-off: dados vs. velocidade
Construir um checklist estruturado a partir de dados de defeitos leva tempo. Você precisa de uma categorização precisa de incidents. Você precisa de disciplina para manter a lista curta. Você precisa de um processo para mantê-la atualizada.
Um checklist genérico leva cinco minutos para escrever e cinco semanas para se tornar irrelevante.
A versão estruturada é mais lenta para criar, mas mais rápida para usar. Um reviewer com quinze perguntas-gatilho específicas escaneia o código de forma mais eficiente do que um reviewer com quarenta lembretes genéricos. A especificidade economiza tempo.
O custo real é organizacional. Você precisa de um registro de defeitos escapados que seja detalhado o suficiente para extrair root causes. Muitas equipes não têm isso. Seu bug tracker tem títulos como „user report: page broken” e nenhuma análise de acompanhamento. Sem dados de root cause, você não pode construir um checklist a partir de evidências. Você só pode copiar o de outra pessoa.
Perguntas frequentes
What is a structured checklist-based review?
Um processo de revisão onde cada inspector usa um checklist, construído a partir de dados reais de defeitos escapados, durante a preparação individual obrigatória antes de uma reunião de inspection em grupo. O checklist contém perguntas-gatilho específicas em vez de lembretes genéricos.
How is this different from a normal code review checklist?
Um checklist normal é frequentemente genérico, copiado de um modelo e consultado casualmente durante a revisão. Um checklist estruturado é derivado do histórico específico de defeitos da sua equipe, limitado ao tipo de artefato e usado durante uma preparação individual focada a uma taxa de leitura definida.
How many items should a structured checklist have?
Dez a quinze itens por tipo de artefato. Mais do que isso e os reviewers começam a percorrer rapidamente. Menos de cinco e você provavelmente está perdendo categorias.
How often should the checklist be updated?
Após cada três a cinco inspections, ou trimestralmente. Remova itens que não tenham produzido descobertas. Adicione itens apenas para novas categorias de defeitos escapados.
Can this work without a full Fagan inspection process?
Sim. O checklist em si é a parte mais portável do método Fagan. Exija preparação individual, dê aos reviewers um checklist estruturado construído a partir dos seus dados, e execute uma reunião time-boxed com um reader. Você detectará mais defeitos do que com uma revisão informal, mesmo sem a cerimônia completa de seis fases.
Comece com um module e um trimestre
Você não precisa reformular todo o seu processo de revisão. Escolha um module que teve defeitos em produção nos últimos seis meses. Reúna as root causes. Construa um checklist de cinco itens. Exija que dois reviewers passem trinta minutos com o código e o checklist antes de qualquer discussão em grupo.
Registre o que eles encontram. Compare com o que seu code review normal de pull request detectou no mesmo código. Essa única comparação dirá se um checklist estruturado vale o esforço para sua equipe.
Se os dados disserem sim, expanda para mais um module. Se os dados disserem não, sua revisão informal já é boa o suficiente, ou seus dados de defeitos não são detalhados o suficiente para construir um checklist útil. De qualquer forma, você terá uma medição real em vez de uma opinião emprestada.