Code review y mentoría
Qué es una revisión de código, cómo se hace bien y por qué hace crecer a los equipos.
Definición
Una revisión de código (code review) es la práctica de que otra persona lea y apruebe un cambio de código antes de integrarlo a la rama principal. Sirve para detectar errores, mantener el código legible y consistente y, sobre todo, para que el conocimiento circule: quien revisa aprende el cambio y quien lo escribió aprende del revisor. Bien hecha, cada revisión es también una sesión breve de mentoría (mentoring).
En Google, según el libro Software Engineering at Google (capítulo 9), prácticamente todo cambio se revisa antes de entrar al repositorio. El estudio Modern Code Review: A Case Study at Google (Sadowski et al., ICSE-SEIP 2018) analizó los registros de 9 millones de cambios revisados, además de 12 entrevistas y una encuesta a 44 desarrolladores, y concluyó que en Google la revisión no gira en torno a encontrar errores: se introdujo para asegurar legibilidad y mantenibilidad, y hoy los desarrolladores valoran además su papel educativo, mantener normas, evitar accidentes y dejar historial.
El flujo con pull request
La revisión moderna es ligera y se hace con herramientas, no en reuniones. En GitHub el cambio se propone con un pull request (en GitLab, merge request):
- El autor trabaja en una rama, hace el cambio pequeño y enfocado y abre el pull request con una descripción: qué cambia, por qué y cómo se probó (si hubo design doc, día 17, lo enlaza).
- La integración continua (CI, día 7) corre pruebas, linter y análisis automático. Si algo falla, el autor lo corrige antes de pedir revisión humana.
- Uno o más revisores comentan línea por línea y dan un veredicto: aprobar, comentar o pedir cambios.
- El autor responde cada comentario: corrige, explica o abre un cambio aparte para lo que no cabe en este.
- Con la aprobación requerida (en muchos equipos, además, la del dueño del código), el cambio se integra.
Qué revisa un revisor
La guía pública de Google para revisores (eng-practices) ordena así lo que hay que mirar:
| Aspecto | Pregunta del revisor |
|---|---|
| Diseño | ¿El cambio tiene sentido en este sistema y en este momento? |
| Funcionalidad | ¿Hace lo que el autor pretende y es bueno para sus usuarios? ¿Hay casos límite o concurrencia mal resuelta? |
| Complejidad | ¿Es más complejo de lo necesario? ¿Implementa cosas que «tal vez» se necesiten después? |
| Pruebas | ¿Tiene pruebas adecuadas y bien diseñadas, que fallarían si el código se rompe? |
| Nombres | ¿Los nombres dicen claramente qué es cada cosa? |
| Comentarios | ¿Son claros y explican sobre todo por qué, no qué? |
| Estilo y consistencia | ¿Sigue la guía de estilo y las convenciones del código existente? |
| Documentación | ¿Se actualizó la documentación que el cambio afecta? |
La guía añade dos reglas: leer cada línea que te asignaron y decir también lo que está bien hecho.
Cambios pequeños y respuestas rápidas
| Criterio | Referencia |
|---|---|
| Tamaño razonable | Guía de Google: unas 100 líneas suele ser razonable y 1,000 suele ser demasiado; Software Engineering at Google habla de unas 200 líneas como límite general de un cambio «pequeño» |
| Tamaño real en Google | Mediana de 24 líneas modificadas por cambio; más del 35 % toca un solo archivo (Sadowski et al.) |
| Tiempo de respuesta | Guía de Google: un día hábil como máximo para responder una solicitud de revisión |
| Latencia real en Google | Primera respuesta en una mediana de menos de una hora para cambios pequeños; proceso completo en menos de 4 horas de mediana (Sadowski et al.) |
Un cambio pequeño se revisa antes y mejor, tiene menos errores, se revierte con facilidad y, si se rechaza, se pierde menos trabajo. Si un pull request crece, se divide: primero la refactorización, después la función nueva.
Cómo escribir comentarios útiles
Un buen comentario tiene tres partes: qué observas, por qué importa y una propuesta concreta. Además, deja claro su nivel, para que el autor sepa qué es obligatorio:
| Nivel | Significado | Ejemplo de inicio |
|---|---|---|
| Bloquea | Debe corregirse antes de integrar: error, riesgo de seguridad, pérdida de datos | «Bloquea: si el cliente reintenta…» |
| Sugerencia | Mejora recomendable; el autor puede decidir | «Sugerencia: esta función se podría…» |
| Nit (detalle) | Menor: nombre, formato, orden. No impide aprobar | «Nit: pago es una intención de pago…» |
| FYI (para tu información) | Algo para considerar en el futuro, no en este cambio | «FYI: existe una utilidad para esto en…» |
La guía de Google pide comentar el código, nunca a la persona, y explicar el porqué. Compara:
Malo: Esto está mal, obviamente el dinero no se maneja así.
Bueno: Bloquea: `total * 0.2` usa punto flotante y con 999.99 da 199.99800000000002,
porque 0.2 no es exacto en binario. Propuesta: usar Decimal y redondear a centavos.
El primero no dice qué está mal ni cómo arreglarlo, y «obviamente» hace sentir mal a quien empieza. El segundo da el dato reproducible, la causa y la salida.
Cómo recibir una revisión
- Los comentarios son sobre el código, no sobre ti. Agradece y responde todos, aunque sea con «hecho»; lo que no cabe en este cambio se anota y se hace en otro.
- Si no estás de acuerdo, explica tu razonamiento con datos; si no hay acuerdo, se decide en una conversación breve, no en veinte réplicas.
- Si un revisor no entendió algo, mejora el código o agrega un comentario en él: el siguiente lector tendrá la misma duda.
Revisión automatizada y sus límites
| Herramienta | Qué hace bien | Qué no sustituye |
|---|---|---|
| Formateadores y linters (Black, Ruff, ESLint, Prettier) | Estilo y errores comunes, sin discusión | El diseño |
| Pruebas en CI (día 7) | Detectar regresiones en cada cambio | Pruebas que nadie escribió |
| Análisis estático y de seguridad | Patrones peligrosos, dependencias vulnerables | El contexto del negocio |
| Revisión asistida por IA | Primer filtro: casos límite, nombres, pruebas faltantes | La responsabilidad de aprobar y el aprendizaje del equipo |
Regla práctica: todo lo que una herramienta puede comprobar no debe ocupar el tiempo de una persona. Los comentarios de una IA se tratan como sugerencias de un revisor más: se verifican, no se aceptan a ciegas.
Mentoría y transferencia de conocimiento
El estudio de Google encontró que los desarrolladores con menos de un año en la empresa suelen recibir más del doble de comentarios por cambio, y que esa cifra baja con la experiencia: la revisión funciona como formación continua. Google además formaliza la legibilidad (readability): una certificación por lenguaje, y todo cambio necesita la aprobación de alguien que la tenga.
Para aprovecharlo: asigna a quien empieza también como revisor, no solo como autor (leer código ajeno enseña tanto como recibir comentarios); en comentarios didácticos, enlaza la guía o la documentación en lugar de solo dar la respuesta, y reconoce de forma explícita lo bien hecho.
Métricas sanas y antipatrones
| Métrica sana | Antipatrón |
|---|---|
| Tiempo hasta la primera respuesta | Usar el número de comentarios como medida de calidad de una persona |
| Tamaño de los cambios | Pull requests de miles de líneas aprobados sin leer («LGTM» automático) |
| Rondas de revisión por cambio | Retrasar la aprobación o exigir cambios por preferencias personales (abuso de poder) |
| Defectos encontrados después de integrar | Discutir el estilo a mano en lugar de configurar un formateador |
Práctica: comprobar tus comentarios antes de enviarlos
El script lee un diff unificado de ejemplo (el pull request que implementa el design doc del día 17), calcula qué líneas cambiaron y revisa cada comentario: que apunte a una línea cambiada, que tenga nivel, porqué y propuesta, y que no use frases duras.
1. Guarda el script como revisa_comentarios.py:
"""Revisa comentarios de code review antes de enviarlos: línea cambiada, nivel (Bloquea,
Sugerencia, Nit), porqué y propuesta, sin frases duras. Uso: python revisa_comentarios.py malos|buenos
"""
import re
import sys
DIFF = """\
--- a/reservas/pagos.py
+++ b/reservas/pagos.py
@@ -12,4 +12,7 @@ def crear_reserva(cliente_id, total):
validar_cliente(cliente_id)
- reserva = Reserva(cliente_id=cliente_id, total=total, estado="confirmada")
+ anticipo = total * 0.2
+ reserva = Reserva(cliente_id=cliente_id, total=total, estado="pendiente_pago")
+ pago = proveedor.crear_pago(monto=anticipo)
+ reserva.pago_id = pago["id"]
guardar(reserva)
return reserva
"""
COMENTARIOS = {
"malos": [
("reservas/pagos.py", 13, "Esto está mal, obviamente el dinero no se maneja así."),
("reservas/pagos.py", 15, "Falta la idempotencia."),
("reservas/pagos.py", 17, "Nit: ¿por qué hiciste esto aquí?"),
],
"buenos": [
("reservas/pagos.py", 13, "Bloquea: `total * 0.2` usa punto flotante y con 999.99 da 199.99800000000002, "
"porque 0.2 no es exacto en binario. Propuesta: usar Decimal y redondear a centavos."),
("reservas/pagos.py", 15, "Bloquea: si el cliente reintenta tras un error de red se cobra dos veces, porque "
"crear_pago no lleva llave de idempotencia. Propuesta: pasar idempotency_key=reserva.id."),
("reservas/pagos.py", 16, "Nit: `pago` es una intención de pago, no un pago aprobado; para no confundirlo, "
"propuesta: renombrarlo a `intento_pago`."),
],
}
NIVELES = re.compile(r"^(bloquea|sugerencia|nit):", re.IGNORECASE)
PORQUE = re.compile(r"\b(porque|ya que|para)\b", re.IGNORECASE)
DURAS = ["esto está mal", "obviamente", "es obvio", "cualquiera sabe", "por qué hiciste", "no sirve", "horrible"]
def lineas_cambiadas(diff: str) -> dict[str, set[int]]:
# Números de línea (en la versión nueva) de cada línea agregada o modificada.
cambiadas, archivo, n = {}, None, 0
for linea in diff.splitlines():
if linea.startswith("+++ "):
archivo = linea[4:].removeprefix("b/")
cambiadas[archivo] = set()
elif linea.startswith("--- "):
continue
elif m := re.match(r"@@ -\d+(?:,\d+)? \+(\d+)", linea):
n = int(m[1])
elif linea.startswith("+"):
cambiadas[archivo].add(n)
n += 1
elif not linea.startswith("-"):
n += 1 # línea de contexto: existe en la versión nueva pero no cambió
return cambiadas
def revisar(archivo: str, linea: int, texto: str, cambiadas: dict[str, set[int]]) -> list[str]:
t = texto.lower()
reglas = [(linea in cambiadas.get(archivo, set()), "no apunta a una línea cambiada"),
(NIVELES.match(texto), "sin nivel (Bloquea, Sugerencia o Nit)"),
(PORQUE.search(texto), "no explica el porqué"),
("propuesta:" in t, "sin propuesta")]
return [msg for ok, msg in reglas if not ok] + [f"frase dura: «{f}»" for f in DURAS if f in t]
if __name__ == "__main__":
cambiadas = lineas_cambiadas(DIFF)
print("Líneas cambiadas:", {a: sorted(n) for a, n in cambiadas.items()})
total = 0
for archivo, linea, texto in COMENTARIOS[sys.argv[1]]:
problemas = revisar(archivo, linea, texto, cambiadas)
total += len(problemas)
print(f"{'OK ' if not problemas else 'FALLA'} {archivo}:{linea} {'; '.join(problemas)}".rstrip())
print(f"Resultado: {total} problema(s)" if total else "Resultado: comentarios listos para enviar")
sys.exit(1 if total else 0)
2. Ejecútalo con los comentarios malos: python revisa_comentarios.py malos. Salida real (Python 3.12, Windows), con código de salida 1:
Líneas cambiadas: {'reservas/pagos.py': [13, 14, 15, 16]}
FALLA reservas/pagos.py:13 sin nivel (Bloquea, Sugerencia o Nit); no explica el porqué; sin propuesta; frase dura: «esto está mal»; frase dura: «obviamente»
FALLA reservas/pagos.py:15 sin nivel (Bloquea, Sugerencia o Nit); no explica el porqué; sin propuesta
FALLA reservas/pagos.py:17 no apunta a una línea cambiada; no explica el porqué; sin propuesta; frase dura: «por qué hiciste»
Resultado: 12 problema(s)
3. Ejecútalo con los buenos: python revisa_comentarios.py buenos. Salida real, con código de salida 0:
Líneas cambiadas: {'reservas/pagos.py': [13, 14, 15, 16]}
OK reservas/pagos.py:13
OK reservas/pagos.py:15
OK reservas/pagos.py:16
Resultado: comentarios listos para enviar
4. Interpreta el resultado. El diff solo cambió las líneas 13 a 16; la línea 17 (guardar(reserva)) es contexto, así que un comentario ahí no corresponde a este cambio. Los comentarios buenos señalan dos defectos reales que bloquean (redondeo de dinero y cobro duplicado) y un detalle de nombre marcado como «Nit». Para usarlo con un pull request real, sustituye DIFF por la salida de git diff main...HEAD.
Skill gratis para Claude: revisa-mi-codigo revisa tus cambios como un mentor: corre tus pruebas y tu linter, comenta línea por línea con el porqué y una propuesta, y comprueba con un script que cada comentario apunte a una línea real del cambio.
Preguntas frecuentes
¿Qué es un code review?
Es cuando otra persona lee tus cambios de código antes de integrarlos para detectar errores, proponer mejoras y mantener el código legible. Se hace con un pull request: una solicitud para integrar los cambios de tu rama que muestra las diferencias línea por línea.
¿Qué significa «Nit» en una revisión de código?
Marca un comentario menor (un nombre, un formato) que conviene corregir pero no impide aprobar el cambio. Etiquetar el nivel evita confundir detalles con problemas graves.
¿La IA puede sustituir la revisión de código?
No. Sirve como primer filtro y detecta problemas comunes, pero no conoce todo el contexto del negocio ni asume la responsabilidad de aprobar, y no transmite conocimiento entre las personas del equipo.
En el reel lo explicamos así
En los buenos equipos nadie publica código sin que otra persona lo revise. Es como un editor de libros: no reescribe tu libro, te ayuda a que quede mejor. Se hace con un pull request: pides integrar tus cambios y alguien los lee línea por línea. Un mal comentario dice «esto está mal»; uno bueno dice qué, por qué y propone cómo. En Google prácticamente todo cambio se revisa antes de entrar. Así aprenden los que empiezan: cada revisión es una mini clase (mentoría). Con esto terminan los 18 días.
Lo que vimos en los 18 días
- ¿Qué es una API?
- Cómo se construye una API (Python y FastAPI)
- Bases de datos: SQL y NoSQL
- Monolito o microservicios
- Docker y contenedores
- Eventos y mensajería
- CI/CD: publicar sin romper nada
- Kubernetes
- Modelos de lenguaje: cómo «piensa» ChatGPT
- Prompts: cómo hablarle a una IA
- RAG: IA que lee tus documentos
- LangChain, LlamaIndex y LangGraph
- Agentes de IA
- Observabilidad: ver qué hace tu IA por dentro
- Velocidad y costo de una app de IA
- MLOps: mantener viva una IA
- PRDs, RFCs y design docs: los planos del software
- Code review y mentoría
Referencias
- Manshreck y Sadowski, Software Engineering at Google, cap. 9: «Code Review».
- Sadowski, Söderberg, Church, Sipko y Bacchelli, «Modern Code Review: A Case Study at Google» (ICSE-SEIP 2018).
- Google, eng-practices: qué buscar en una revisión.
- Google, eng-practices: cómo escribir comentarios y velocidad de las revisiones.
- Google, eng-practices: cambios pequeños.
- GitHub Docs, About pull requests.
Un editor de libros: no reescribe tu libro, te ayuda a que quede mejor.
Glosario: del inglés al español
| Code review | revisión de código |
|---|---|
| Pull request | solicitud para integrar cambios |
| Feedback | retroalimentación |
| Mentoring | mentoría |