La mayoría de las checklists de revisión son placebos
Si tu equipo tiene una checklist para code review, hay una buena probabilidad de que viva en una página wiki que nadie abre. Probablemente dice cosas como „check for off-by-one errors” y „verify error handling.” Esas son afirmaciones verdaderas. También son demasiado vagas para cambiar el comportamiento.
Una checklist que te dice que hagas „check for bugs” no es una checklist. Es un recordatorio de que los bugs existen.
La checklist en una Fagan inspection cumple un propósito diferente. No es una lista de cosas de las que preocuparse. Es una herramienta estructurada que dirige la attention del reviewer hacia categorías específicas de defectos que han sido observadas en el codebase real que se está revisando. Se construye a partir de datos, se adapta al tipo de artefacto y se utiliza durante la preparación individual obligatoria. Cuando se usa correctamente, es una de las principales razones por las que una inspection estructurada detecta de tres a cuatro veces más defectos que una revisión informal.
Qué hace que una checklist sea „estructurada”
La palabra „estructurada” importa aquí. Una checklist estructurada no es una checklist más larga. Es una checklist con propiedades de diseño específicas.
Primero, se deriva de datos de defectos escapados. Los ítems provienen de bugs que realmente llegaron a producción, no de un documento genérico de mejores prácticas. Si tus últimos tres incidents involucraron race conditions en async cleanup, esa categoría obtiene su propio ítem en la checklist. Si los null dereferences no han sido un problema en dos años, ese ítem se elimina.
Segundo, está limitada al tipo de artefacto que se está revisando. Las Fagan inspections originalmente usaban diferentes checklists para documentos de requisitos, documentos de diseño, source code y planes de prueba. Cada artefacto tiene diferentes categorías de defectos. Una checklist de documento de diseño pregunta sobre consistencia de interfaces y acoplamiento. Una checklist de source code pregunta sobre boundary conditions y limpieza de recursos. Mezclar ambas diluye ambas.
Tercero, se usa durante la preparación individual, no durante la reunión. Cada inspector lee el material solo, con la checklist, antes de que el grupo se reúna. La checklist moldea lo que cada persona ve cuando lee el código de forma aislada. No es un documento de referencia compartido. Es una lente personal.
Cuarto, es lo suficientemente corta para ser usable. Una checklist con cuarenta ítems es un catálogo, no una herramienta. Fagan recomendaba aproximadamente de diez a quince ítems por tipo de artefacto. La restricción fuerza la priorización. Mantienes las categorías que importan y descartas el ruido.
Cómo construir una a partir de tus propios datos
La mejor manera de construir una checklist estructurada es observar qué ya salió mal. Aquí hay un proceso práctico.
Comienza con tu incident tracker, tu bug database o tus notas de postmortem. Extrae los últimos veinte defectos que escaparon de la revisión y llegaron a producción o pruebas. Clasifica cada uno por root cause, no por síntoma. „Page crashed” es un síntoma. „Missing null check after external API response” es una root cause.
Agrupa las root causes en categorías. Probablemente descubrirás que el 80 por ciento de tus defectos escapados caen en tres a cinco categorías. Esas son tus ítems de checklist.
Para cada categoría, escribe una pregunta desencadenante específica, no un recordatorio vago. „Check for nulls” es vago. „For every function that calls an external API, verify the response is validated before use” es una pregunta desencadenante. Le dice al reviewer exactamente qué buscar y dónde buscar.
Aquí hay un ejemplo concreto. Supongamos que tu equipo hace deploy de servicios en Python y analizaste los últimos veinte issues de producción. Encuentras esta distribución:
- 6 issues: falta de manejo de errores en llamadas externas
- 5 issues: límites de transacciones de base de datos incorrectos
- 4 issues: off-by-one en lógica de paginación
- 3 issues: race conditions en datos en caché
- 2 issues: registry de datos sensibles
Tu checklist debería tener cinco ítems, uno por cada categoría. Cada ítem debería ser una pregunta desencadenante vinculada a tus 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}")
Ejecuta este script contra tus propios datos de bugs y tendrás una checklist vinculada a tus failure modes reales, no a los de otra persona.
Cómo se ve realmente la sesión de revisión
Con la checklist en mano, la sesión de revisión tiene un ritmo específico.
Durante la preparación individual, cada inspector lee el material solo a aproximadamente 100 a 150 líneas por hora. Usan la checklist como una lente, no como un guion. No trabajan la checklist ítem por ítem en orden. Leen el código de forma natural, y cuando encuentran un pattern que coincide con una categoría de la checklist, hacen una pausa y lo examinan cuidadosamente.
La checklist no reemplaza la lectura. Es un pattern matcher para cosas que tu cerebro probablemente omita.
En el inspection meeting, el reader parafrasea el código en voz alta. Cuando un inspector detecta un defecto potencial, lo plantea inmediatamente. Las categorías de la checklist proporcionan un vocabulario compartido. En lugar de decir „esto se ve mal”, un inspector puede decir „esto parece un error de límite de transaction, categoría dos.” El moderator lo registra. El author escucha. Nadie propone una solución.
La checklist no se query durante la reunión. En ese punto, los inspectors ya la han interiorizado. La reunión es para verificar cruzadamente lo que cada persona encontró de forma aislada.
Por qué fallan las checklists genéricas
La mayoría de los equipos que prueban las checklists se rinden porque usan el tipo equivocado.
Una checklist genérica copiada de un blog post no tiene peso emocional. Le pide al reviewer que busque cosas que quizás nunca hayan ocurrido en su codebase. El reviewer la revisa por encima, no ve nada familiar y vuelve a leer el diff como siempre lo ha hecho.
Una checklist estructurada construida a partir de defectos escapados es diferente. Cada ítem representa un incident real que costó tiempo real. El reviewer conoce estos bugs porque ha visto los postmortems. La checklist conecta el comportamiento de revisión con fallas específicas y memorables.
El otro error común es tratar la checklist como una herramienta de reunión en lugar de una herramienta de preparación. Si sacas la checklist durante la reunión de revisión, se convierte en un guion para un repaso grupal. Todos leen el mismo ítem, miran el mismo código y convergen en las mismas observaciones obvias. El poder de la checklist está en la preparación individual, donde seis personas aplican las mismas categorías de forma independiente y encuentran cosas diferentes.
La carga de mantenimiento que la mayoría de los equipos ignora
Una checklist no es un monumento. Es un documento vivo que se deteriora más rápido que el código.
Si agregas un ítem a la checklist cada vez que algo sale mal pero nunca eliminas uno, tendrás cuarenta ítems en seis meses. En ese punto, los reviewers empiezan a tratarla como papel tapiz.
Fagan recomendó revisar la checklist misma después de cada pocas inspections. Elimina los ítems que no hayan generado un hallazgo en las últimas cinco revisiones. Agrega ítems solo para nuevas categorías de defectos que hayan escapado a producción. Mantén el total por debajo de quince. Si no puedes mantenerla corta, no estás priorizando.
Aquí hay un script ligero para podar una checklist basándose en datos históricos de hallazgos:
#!/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}")
Programa esta revisión trimestralmente. Una checklist obsoleta es peor que ninguna porque entrena a los reviewers para que ignoren la herramienta.
El compromiso: datos vs. velocidad
Construir una checklist estructurada a partir de datos de defectos lleva tiempo. Necesitas una categorización precisa de incidents. Necesitas disciplina para mantener la lista corta. Necesitas un proceso para mantenerla actualizada.
Una checklist genérica tarda cinco minutos en escribirse y cinco semanas en volverse irrelevante.
La versión estructurada es más lenta de crear pero más rápida de usar. Un reviewer con quince preguntas desencadenantes específicas escanea el código de forma más eficiente que un reviewer con cuarenta recordatorios genéricos. La especificidad ahorra tiempo.
El costo real es organizativo. Necesitas un registry de defectos escapados que sea lo suficientemente detallado para extraer root causes. Muchos equipos no lo tienen. Su bug tracker tiene títulos como „user report: page broken” y sin análisis de tracing. Sin datos de root cause, no puedes construir una checklist a partir de evidencia. Solo puedes copiar la de otra persona.
Preguntas frecuentes
What is a structured checklist-based review?
Un proceso de revisión en el que cada inspector utiliza una checklist, construida a partir de datos reales de defectos escapados, durante la preparación individual obligatoria antes de una reunión de inspection grupal. La checklist contiene preguntas desencadenantes específicas en lugar de recordatorios genéricos.
How is this different from a normal code review checklist?
Una checklist normal suele ser genérica, copiada de una plantilla y consultada de forma casual durante la revisión. Una checklist estructurada se deriva del historial específico de defectos de tu equipo, está limitada al tipo de artefacto y se utiliza durante una preparación individual enfocada a una tasa de lectura definida.
How many items should a structured checklist have?
De diez a quince ítems por tipo de artefacto. Más que eso y los reviewers empiezan a revisar por encima. Menos de cinco y probablemente te falten categorías.
How often should the checklist be updated?
Después de cada tres a cinco inspections, o trimestralmente. Elimina los ítems que no hayan producido hallazgos. Agrega ítems solo para nuevas categorías de defectos escapados.
Can this work without a full Fagan inspection process?
Sí. La checklist en sí es la parte más portable del método Fagan. Exige preparación individual, dale a los reviewers una checklist estructurada construida a partir de tus datos, y realiza una reunión time-boxed con un reader. Detectarás más defectos que con una revisión informal incluso sin la ceremonia completa de seis fases.
Empieza con un module y un trimestre
No necesitas rehacer todo tu proceso de revisión. Elige un module que haya tenido defectos en producción en los últimos seis meses. Recolecta las root causes. Construye una checklist de cinco ítems. Exige que dos reviewers pasen treinta minutos con el código y la checklist antes de cualquier discusión grupal.
Registra lo que encuentran. Compáralo con lo que tu code review normal de pull request detectó en el mismo código. Esa única comparación te dirá si una checklist estructurada vale el esfuerzo para tu equipo.
Si los datos dicen que sí, expande a un module más. Si los datos dicen que no, tu revisión informal ya es lo suficientemente buena, o tus datos de defectos no son lo suficientemente detallados para construir una checklist útil. De cualquier forma, tendrás una medición real en lugar de una opinión prestada.