Plantilla de Checklist para Code Review
Checklist estructurada para revisiones de código consistentes y exhaustivas que detectan bugs, mejoran la legibilidad y comparten conocimiento en el equipo.
Descripción General
Una checklist de code review es un conjunto estandarizado de verificaciones que un revisor aplica a cada pull request, cubriendo las dimensiones que le importan al equipo: correctitud, seguridad, rendimiento, tests, estilo, documentación y operaciones. Es el filtro de calidad más útil en la entrega de software, pero solo cuando es consistente. Sin una checklist, los revisores se enfocan en lo que les importa personalmente: un ingeniero verifica inyección SQL, otro se obsesiona con nombres de variables y un tercero solo mira cobertura de tests. Una checklist compartida garantiza que cada revisión cubra lo que le importa al equipo, dejando espacio para el juicio humano en diseño y arquitectura.
Esta plantilla cubre el lado del revisor en el flujo de trabajo. Combínala con una plantilla de pull request para que los autores entreguen PRs listos para revisión, y consulta la guía de buenas prácticas de code review para el proceso alrededor: carga de revisores, tiempos de respuesta esperados y cómo dar feedback que funcione. Para cambios que tocan autenticación, pagos o datos de usuario, súmale la checklist de revisión de seguridad para PRs.
Cuándo Usar
Usa esta plantilla cuando:
- Quieras estandarizar qué significa “listo” para la revisión de código en el equipo
- Los nuevos revisores no estén seguros de qué buscar en un pull request
- Las revisiones tarden demasiado porque los revisores no conocen el alcance
- Estés integrando un equipo nuevo y necesites establecer estándares compartidos rápidamente
- Quieras reducir defectos post-merge e incidentes en producción
Omite la checklist completa cuando solo serviría para que la gente la ignore: repos con un único maintainer que revisa sus propios PRs, cambios de typos o solo comentarios, y PRs de configuración que CI ya valida de punta a punta. Un proceso pesado sobre cambios triviales es exactamente cómo mueren las checklists.
Prerrequisitos
Antes de adoptar esta checklist:
- El equipo acuerda qué ítems son obligatorios vs. opcionales
- CI está configurado para detectar problemas automatizables (linting, formato, verificaciones de tipos)
- Los revisores entienden que las checklists complementan el juicio; no lo reemplazan
- Existe una ruta documentada para escalar desacuerdos entre autor y revisor
- La checklist está guardada donde los revisores puedan consultarla fácilmente (plantilla de PR, wiki o mensaje fijado)
Solución
# Checklist de Code Review
> Revisor: ______ | Autor: ______ | PR: ______ | Fecha: ______
## 1. Lógica y Correctitud
- [ ] El código hace lo que la descripción del PR dice que hace
- [ ] Los casos borde están manejados (entradas vacías, nulos, timeouts, fallos)
- [ ] No hay errores off-by-one, condiciones de carrera ni bucles infinitos
- [ ] Los caminos de error son explícitos y no silencian excepciones
- [ ] La lógica de negocio coincide con la especificación o el requerimiento del ticket
## 2. Seguridad
- [ ] No hay secretos, tokens ni credenciales en el código
- [ ] Las entradas de usuario están validadas y sanitizadas
- [ ] Existen verificaciones de autorización para operaciones protegidas
- [ ] No hay vulnerabilidades de inyección SQL, XSS ni command injection
- [ ] Las dependencias agregadas están verificadas y provienen de fuentes confiables
## 3. Rendimiento
- [ ] No hay consultas N+1 ni ineficiencias algorítmicas evidentes
- [ ] Los conjuntos grandes de datos están paginados o transmitidos por streaming
- [ ] No hay llamadas de red innecesarias ni I/O bloqueante en rutas críticas
- [ ] El caching se utiliza apropiadamente donde corresponde
- [ ] Se previenen fugas de recursos (conexiones, descriptores de archivo, memoria)
## 4. Testing
- [ ] Los tests unitarios cubren la lógica nueva o modificada
- [ ] Los tests de integración cubren dependencias externas y límites
- [ ] Los casos borde y caminos de fallo están probados, no solo caminos felices
- [ ] Los tests son deterministas y no dependen de tiempo ni orden
- [ ] Los nombres de los tests describen comportamiento, no implementación
## 5. Mantenibilidad y Estilo
- [ ] El código es legible sin necesidad de preguntarle al autor
- [ ] Los nombres de variables y funciones son descriptivos y consistentes
- [ ] No hay lógica duplicada que podría extraerse o reutilizarse
- [ ] Los comentarios explican el "por qué", no el "qué" (el código muestra el qué)
- [ ] La complejidad es apropiada; se señala la sobre-ingeniería
## 6. Documentación
- [ ] Las APIs públicas tienen documentación actualizada o specs OpenAPI
- [ ] El README o los runbooks se actualizan si cambió el comportamiento
- [ ] Los breaking changes se destacan explícitamente en la descripción del PR
- [ ] Los pasos de migración están documentados si cambió el esquema o la config
## 7. Despliegue y Operaciones
- [ ] Los feature flags se usan para cambios riesgosos o irreversibles
- [ ] El monitoreo y las alertas se agregan o actualizan para los nuevos caminos
- [ ] El procedimiento de rollback se entiende y está probado
- [ ] Las migraciones de base de datos son compatibles hacia atrás o tienen un plan
---
## Notas de Revisión
| Línea / Archivo | Problema | Severidad | Acción |
|-----------------|----------|-----------|--------|
| ______ | ______ | Nit / Sugerencia / Bloqueador | ______ |
**Veredicto final:** Aprobar / Solicitar cambios / Comentar
**Preparación para merge:** Listo / Requiere cambios / Bloqueado
## Confirmación del Revisor
- [ ] Leí cada línea modificada
- [ ] Ejecuté los tests localmente
- [ ] Verifiqué que la descripción del PR coincide con los cambios
- [ ] Revisé breaking changes más allá de lo que declara el autor
Archivos complementarios: el repo companion de code-review-checklist-template tiene esta checklist como archivo independiente más el workflow de automatización, el validador y el script de métricas de la sección Soluciones Avanzadas.
Cómo Encaja la Checklist en el Flujo de Revisión
La checklist se ubica entre las verificaciones automatizadas y la decisión de merge. CI y el análisis estático corren primero y filtran los problemas mecánicos: lint, tipos, tests rotos y patrones de vulnerabilidad conocidos. El revisor después trabaja la checklist sobre lo que queda: lo que necesita juicio. Si el veredicto es “solicitar cambios”, el ciclo se repite en el próximo push.
Dos propiedades de esta división importan. Primero, la checklist es para humanos: todo lo que una máquina puede verificar pertenece a CI, no a la lista — por eso el ítem 4 de “Lo que funciona” retira verificaciones a medida que las herramientas avanzan. Segundo, el ciclo trimestral es lo que mantiene honesta a la checklist: los ítems que nunca se disparan son ruido, y los huecos que sigues encontrando en producción pertenecen a ella.
Cómo Adoptarla
La checklist solo funciona si los revisores la ven sin tener que ir a buscarla. Tres ubicaciones cubren a la mayoría de los equipos:
- Plantilla de PR (recomendado). Pega la checklist en
.github/pull_request_template.mdpara que cada PR la muestre al revisor. Mantén los ítems obligatorios arriba y recorta los opcionales: una plantilla que nadie termina termina omitiéndose. GitHub además permite mantener plantillas de PR separadas en.github/PULL_REQUEST_TEMPLATE/si quieres una variante corta para docs o cambios de configuración. - Wiki o handbook de ingeniería. Si tu plantilla de PR ya está cargada, enlaza la checklist desde ella y guarda la versión canónica en el handbook. El costo es real: los revisores tienen que hacer clic para llegar, así que espera menor adopción.
- CODEOWNERS para secciones con restricción. Para la variante crítica de seguridad de la tabla de abajo, usa
CODEOWNERSpara dirigir los cambios en auth, criptografía o pagos a revisores que realmente puedan aprobarlos — la checklist sola no puede imponer eso.
Cualquiera sea la ubicación, acuerda las expectativas de tiempo de respuesta de antemano. Una base común es primera revisión dentro del día hábil para PRs de menos de ~400 líneas modificadas; los PRs más grandes reciben una revisión por etapas o un pedido amable de dividirlos. Combinar la checklist con una rotación de revisores reparte la carga y evita que una persona senior se convierta en el guardián permanente.
Ejemplo: Una Revisión Completada
La tabla de Notas de Revisión es lo que convierte una checklist en una conversación. Así se ve una entrada real en un PR que agrega un endpoint de usuarios:
| Línea / Archivo | Problema | Severidad | Acción |
|---|---|---|---|
users.py:87 | email escrito en logs en texto plano | Bloqueador | Eliminar o enmascarar antes del merge |
UserRepository.find() | `LIKE ’%‘ | name | |
get_user():34 | El comentario repite lo que dice el código | Nit | Limpieza opcional |
Veredicto final: Solicitar cambios. Preparación para merge: Bloqueado hasta corregir los dos bloqueadores. Los nits se corrigen o se descartan explícitamente — de cualquier forma no frenan el merge.
Nota la disciplina: cada bloqueador nombra un archivo y una corrección concreta, así que el autor nunca tiene que adivinar qué significa “solicitar cambios”. Esa es la diferencia entre una checklist que hace avanzar el código y un hilo de comentarios que se estanca dos días.
Explicación
La checklist está organizada por preocupación en lugar de por tipo de archivo. Los revisores tienden a perderse leyendo un diff línea por línea; evaluar el cambio contra dimensiones fijas — correctitud, seguridad, rendimiento, testing, mantenibilidad, documentación, operaciones — obliga a una cobertura completa y produce veredictos comparables entre revisores.
La otra decisión que importa es separar lo que verifican las máquinas de lo que verifican los humanos. El linting, el formato y los errores de tipos son trabajo de CI; ponerlos en una checklist humana desperdicia minutos de revisión y enseña a los revisores a leer por encima. La checklist se mantiene enfocada en lo que los humanos hacen mejor que las herramientas: si la lógica coincide con la especificación, si los tests verifican lo correcto, si el próximo lector va a entender el código.
Variantes
| Contexto | Ajustes | Notas |
|---|---|---|
| Código frontend / UI | Agregar accesibilidad, diseño responsivo y compatibilidad de navegador | Los cambios visuales necesitan verificación manual |
| Pipelines de datos | Agregar verificaciones de calidad de datos, compatibilidad de esquema y consideraciones de backfill | Los cambios de datos son más difíciles de deshacer que los de código |
| Infraestructura / Terraform | Agregar seguridad de estado, revisión del plan y evaluación del radio de impacto | Una mala configuración puede tumbar producción |
| Código crítico de seguridad | Hacer obligatoria la sección de seguridad y exigir aprobación del equipo de seguridad | Finanzas, salud y sistemas de autenticación |
| Hotfix / respuesta a incidente | Acortar la checklist a seguridad, correctitud y rollback solamente | La velocidad importa; documenta lo que se omitió y por qué |
Trata la tabla como punto de partida, no como un fork: a los equipos les va mejor con una checklist más secciones por contexto que con cinco plantillas paralelas que divergen con el tiempo.
Lo que funciona
- Mantén la checklist visible. Incrústala en la plantilla de PR para que los revisores la vean automáticamente
- Distingue nits de bloqueadores. No todo problema impide el merge; usa etiquetas de severidad para mantener las revisiones fluidas
- Rota los revisores. Las checklists reducen la brecha de experiencia y facilitan distribuir la carga de revisión
- Revisa la checklist trimestralmente. Elimina los ítems que ya están automatizados; agrega los que se siguen escapando
- Limita el tiempo de revisión. Si una revisión toma más de 30 minutos, el PR probablemente es demasiado grande
- Mide adopción, no solo presencia. Si menos de la mitad de los PRs mergeados muestran una checklist completada, el proceso es decorativo: averigua dónde se está saltando antes de agregar más ítems
Errores Comunes
- Tratar la checklist como sustituto del pensamiento. La checklist atrapa omisiones comunes, no fallas de diseño
- Hacer cada ítem obligatorio. Esto ralentiza las revisiones sin mejorar la calidad; bloquea solo en correctitud, seguridad y tests
- No actualizar la checklist. A medida que las herramientas mejoran, las verificaciones manuales deberían automatizarse
- Revisar en soledad. Las revisiones en pareja sobre cambios críticos atrapan problemas que los revisores solitarios omiten
- Enfocarse solo en el diff. Los revisores también deberían verificar que la descripción del PR, los tests y la documentación sean consistentes
- Revisar el código de test con menos rigor que el de producción. Tests frágiles, no deterministas o que no verifican lo correcto dan falsa confianza — aplica la misma checklist a los archivos de test:
# Anti-patrón: test que siempre pasa
def test_user_creation():
user = create_user(email="test@example.com")
assert user is not None # No verifica email, id ni status
# Mejor: test que verifica comportamiento
def test_user_creation_sets_correct_fields():
user = create_user(email="test@example.com")
assert user.email == "test@example.com"
assert user.id is not None
assert user.status == "active"
assert user.created_at is not None
Soluciones Avanzadas
Revisión de código automatizada con GitHub Actions y CodeQL
Combina la checklist manual con análisis automatizado para detectar problemas que los humanos pasan por alto. Este workflow corre CodeQL para patrones de vulnerabilidad, exige lint y verificaciones de tipos, y avisa en PRs demasiado grandes: los tres ítems automatizables que liberan al revisor para enfocarse en la checklist misma:
# .github/workflows/code-review-automation.yml
name: Automated Code Review
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
codeql-analysis:
runs-on: ubuntu-latest
permissions:
security-events: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: python, javascript
# autobuild solo es necesario para lenguajes compilados
- uses: github/codeql-action/analyze@v3
lint-and-type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- name: Comment PR with lint results
if: failure()
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: "Linting or type checking failed. Please fix before requesting review."
})
pr-size-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check PR size
uses: actions/github-script@v7
with:
script: |
// paginate: listFiles devuelve 30 archivos por página por defecto
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
per_page: 100,
});
const totalChanges = files.reduce(
(sum, file) => sum + file.changes, 0
);
if (totalChanges > 500) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `This PR has ${totalChanges} changes. Consider splitting into smaller PRs for easier review.`
});
}
Bot de revisión para hacer cumplir la checklist
Verifica automáticamente que los PRs cumplan los requisitos de la checklist antes de la revisión. El validador de abajo chequea tres reglas mecánicas — tests agregados junto al código nuevo, ausencia de strings que parecen secretos en las líneas añadidas y una descripción de PR completa — que son exactamente el tipo de verificaciones que desperdician atención del revisor cuando se hacen a mano:
import re
import subprocess
from typing import Dict, List
class PRChecklistValidator:
def __init__(self, pr_diff: str, pr_description: str):
self.diff = pr_diff
self.description = pr_description
def check_tests_added(self) -> bool:
"""Verify that new code includes tests."""
has_source = bool(re.search(r'\+\+\+ b/src/.*\.py', self.diff))
has_tests = bool(re.search(r'\+\+\+ b/tests/.*\.py', self.diff))
if has_source and not has_tests:
return False
return True
def check_no_secrets(self) -> bool:
"""Check for common secret patterns in the diff."""
secret_patterns = [
r'(?i)api[_-]?key\s*=\s*["\'][^"\']+["\']',
r'(?i)password\s*=\s*["\'][^"\']+["\']',
r'(?i)secret\s*=\s*["\'][^"\']+["\']',
r'-----BEGIN (RSA |EC )?PRIVATE KEY-----',
r'(?i)aws_secret_access_key\s*=\s*["\'][^"\']+["\']',
]
added_lines = [line for line in self.diff.split('\n') if line.startswith('+')]
for line in added_lines:
for pattern in secret_patterns:
if re.search(pattern, line):
return False
return True
def check_pr_description(self) -> bool:
"""Verify PR description has required sections."""
required_sections = ["## Summary", "## Testing", "## Breaking Changes"]
return all(section in self.description for section in required_sections)
def run_all_checks(self) -> List[Dict]:
"""Run all checklist validations and return results."""
checks = [
("Tests added for new code", self.check_tests_added),
("No secrets in diff", self.check_no_secrets),
("PR description complete", self.check_pr_description),
]
results = []
for name, check_fn in checks:
passed = check_fn()
results.append({
"check": name,
"passed": passed,
"status": "PASS" if passed else "FAIL",
})
return results
# Ejemplo de uso: diff y descripción de la rama actual vs main
diff = subprocess.run(
["git", "diff", "origin/main...HEAD"],
capture_output=True, text=True
).stdout
pr_description = subprocess.run(
["git", "log", "-1", "--pretty=%B"],
capture_output=True, text=True
).stdout
validator = PRChecklistValidator(diff, pr_description)
results = validator.run_all_checks()
for r in results:
print(f"{r['status']}: {r['check']}")
Dashboard de métricas de revisión
Rastrea tiempo de ciclo de revisión, calidad de comentarios y tasa de escape de defectos. El tiempo de ciclo te dice si las revisiones son un cuello de botella; la tasa de escape te dice si la checklist realmente está atrapando cosas o solo agregando ceremonia:
from collections import defaultdict
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Dict, List
@dataclass
class ReviewMetric:
pr_number: int
author: str
reviewer: str
created_at: datetime
merged_at: datetime
comments_count: int
blockers_count: int
post_merge_defects: int
def calculate_review_metrics(metrics: List[ReviewMetric]) -> Dict:
"""Calculate aggregate review metrics for the team."""
if not metrics:
return {}
cycle_times = [(m.merged_at - m.created_at).total_seconds() / 3600 for m in metrics]
comment_counts = [m.comments_count for m in metrics]
defect_rate = sum(1 for m in metrics if m.post_merge_defects > 0) / len(metrics)
by_reviewer = defaultdict(list)
for m in metrics:
by_reviewer[m.reviewer].append(m)
reviewer_stats = {}
for reviewer, reviews in by_reviewer.items():
reviewer_stats[reviewer] = {
"total_reviews": len(reviews),
"avg_comments": sum(r.comments_count for r in reviews) / len(reviews),
"avg_blockers": sum(r.blockers_count for r in reviews) / len(reviews),
"defects_missed": sum(r.post_merge_defects for r in reviews),
}
return {
"avg_cycle_time_hours": sum(cycle_times) / len(cycle_times),
"avg_comments_per_pr": sum(comment_counts) / len(comment_counts),
"defect_escape_rate": defect_rate,
"by_reviewer": reviewer_stats,
}
# Ejemplo de uso
metrics = [
ReviewMetric(101, "alice", "bob", datetime.now() - timedelta(hours=5), datetime.now(), 3, 1, 0),
ReviewMetric(102, "charlie", "bob", datetime.now() - timedelta(hours=2), datetime.now(), 5, 2, 1),
ReviewMetric(103, "alice", "dave", datetime.now() - timedelta(hours=8), datetime.now(), 1, 0, 0),
]
results = calculate_review_metrics(metrics)
print(f"Average cycle time: {results['avg_cycle_time_hours']:.1f}h")
print(f"Defect escape rate: {results['defect_escape_rate']:.0%}")
Dos lecturas que vale la pena seguir trimestralmente: una defect_escape_rate a la baja significa que la checklist está funcionando, y una gran dispersión en avg_cycle_time_hours entre revisores suele indicar que la checklist se aplica de forma desigual — un problema de capacitación, no de herramientas.
Lectura Recomendada
- Google Engineering Practices — How to do a code review: la referencia de proceso canónica detrás de la mayoría de las heurísticas de esta página (tamaño de PR, tiempos de respuesta, manejo de nits).
- GitHub Docs — Reviewing proposed changes in a pull request: mecánica de las revisiones de PR, sugerencias de cambios y aprobaciones requeridas.
- Documentación de CodeQL: referencia de queries para extender el análisis automatizado de arriba más allá de las suites por defecto.
Preguntas frecuentes
¿Cuánto debería durar una revisión de código?
Los PRs pequeños (menos de 200 líneas) deberían revisarse en pocas horas. Los PRs grandes deberían dividirse o revisarse por etapas. Si una revisión consistentemente toma más de 30 minutos, el equipo debería investigar si los PRs son demasiado grandes o la checklist es demasiado amplia.
¿Deberían los ingenieros junior revisar código de ingenieros senior?
Sí. La revisión de código es una oportunidad de aprendizaje tanto como un filtro de calidad. Los revisores junior detectan problemas de claridad que los senior pasan por alto porque estos ya comparten las suposiciones del autor. La checklist pone a todos en igualdad de condiciones al indicarle a cada uno qué verificar.
¿Qué pasa si el autor no está de acuerdo con un comentario de revisión?
Que lo discutan. Si la conversación se estanca, escala a un tech lead o al criterio de desempate documentado del equipo. La checklist existe para reducir debates subjetivos haciendo las expectativas explícitas, pero no puede eliminar todo desacuerdo.
¿Cuál es la diferencia entre un nit y un bloqueador?
Un nit es una mejora estilística o menor que el autor puede tomar o dejar: preferencias de nombres, redacción de comentarios, un idioma alternativo. Un bloqueador es un problema de correctitud, seguridad o integridad de datos que debe corregirse antes del merge. Marca la severidad explícitamente en cada comentario; mezclar los dos es lo que hace que las revisiones se sientan arbitrarias.
¿No cubre CI ya esta checklist?
Solo el tercio mecánico. Los linters, los verificadores de tipos, los tests y CodeQL atrapan violaciones de estilo, builds rotos y patrones de vulnerabilidad conocidos. No pueden decir si el código coincide con el ticket, si un test verifica el comportamiento correcto o si una migración es realmente reversible. La checklist cubre las decisiones de juicio — por eso el primer prerrequisito es llevar lo automatizable a CI.
Recursos Relacionados
Mejores Prácticas de Code Review para Autores y Revisores
Una guía práctica para revisiones de código útiles: cómo escribir código revisable, dar feedback constructivo y mantener las revisiones rápidas y enfocadas.
DocPlantilla de Pull Request
Plantilla de pull request completa para estandarizar code reviews y mejorar la calidad de merges.
DocChecklist de Revisión de Seguridad para PRs
Checklist para revisiones de seguridad durante pull requests: validación de entrada, autenticación, autorización, secretos, dependencias, inyección, XSS, CSRF, logging y herramientas automatizadas con ejemplos de código.
DocDocumento de Estrategia de Branching en Git
Una plantilla de documento para definir flujo de trabajo Git, convenciones de ramas, requisitos de merge y procedimientos de release para equipos de ingenieria.
DocPlantilla de Engineering Handbook
Una plantilla para documentar la cultura del equipo, procesos de desarrollo, estandares tecnicos y practicas operacionales en un handbook unico y referenciable.
DocChecklist de Onboarding para Ingenieros Backend
Una checklist completa para incorporar nuevos ingenieros backend que cubre la configuración del entorno, la orientación al codebase, la formación en seguridad y las metas de la primera semana.