English · Español
05 — Una plantilla reusable de postmortem; aplicada a un incidente ficticio del tutor §A13¶
La plantilla de postmortem aquí no es decorativa: cuatro secciones, cada una con un objetivo claro, encuadradas para ser blameless. Aplicarla a un incidente ficticio del tutor §A13 muestra cómo se rellena. Si saltas la plantilla, el mismo incidente recurre — eso es lo que demuestra el
/breakadjunto.
La plantilla (reusable)¶
Un postmortem sin culpas tiene cuatro secciones nombradas. Tope de longitud por sección entre paréntesis.
1. Situación (50-150 palabras)¶
Qué ocurrió, cuál fue el impacto de cara al usuario, cuándo empezó y terminó (UTC). Un párrafo. Prueba de leerlo en voz alta: si lees solo esta sección a un extraño, debería saber el titular.
Artefactos requeridos: captura del panel en el pico de impacto, la alerta que se disparó, el SHA del commit/release vigente al inicio del incidente.
2. Línea de tiempo (cronológica, UTC)¶
Una lista de marcas de tiempo y eventos. Cada entrada tiene una señal: una línea de log, un commit, una alerta, un deploy. Evita estilo narrativo de "entonces nos dimos cuenta" — usa señales objetivas.
El objetivo de la línea de tiempo es hacer visible el retraso de detección y el retraso de mitigación. Ambos son medibles, ambos son mejorables, ambos tienen acciones asociadas.
3. Factores contribuyentes (3-5 elementos)¶
Múltiples. No "la causa raíz". Los incidentes reales tienen múltiples factores contribuyentes — proceso, herramientas, diseño, humano. Listarlos como cadena ("A causó B causó C") suele ser incorrecto; listarlos como conjunto que combinó mal suele ser correcto.
Cada elemento debe ser suficientemente específico como para ser arreglado (no "falta de tests" — eso es una categoría; especifica "la puerta de evaluación no cubría el orden de claves del JSON").
4. Acciones (concretas, con dueño, con fecha)¶
Para cada factor contribuyente, como mucho una acción concreta. Cada elemento tiene:
- Un título (una línea).
- Un dueño (una persona; "Borja" sirve para el currículo).
- Una fecha límite (una fecha específica, no "el próximo trimestre").
- Un criterio de éxito (cómo se ve "hecho"; binario si es posible).
Encuadre sin culpas. Las acciones apuntan al sistema, no a la persona. Mal: "Borja debería tener más cuidado al editar release.sh". Bien: "Añadir un test unitario que falle si el flag --keep de release.sh es < 5".
La lente sin culpas. Personas tomando decisiones bajo incertidumbre con información imperfecta a veces elegirán mal. El postmortem documenta el sistema que les dio información imperfecta y arregla eso. La persona hizo lo mejor que pudo con lo que tenía — esa es la suposición.
Un incidente ficticio — aplicado al tutor de gramática §A13¶
Título¶
INC-2026-08-14-tutor-spanish-gloss-missing — Gloss español ausente en ~30% de las respuestas del tutor durante 47 minutos.
1. Situación¶
El 2026-08-14 entre las 09:31 UTC y las 10:18 UTC, el tutor de gramática §A13 sirvió respuestas HTTP 200 que carecían del campo spanish en ~30% de los casos. La UI de quiz del portal renderizó un gloss español vacío para los elementos afectados, confundiendo a los aprendices durante una clase programada de 28 estudiantes. Sin pérdida de datos. Sin impacto de seguridad. Recuperado vía rollback al release anterior.
2. Línea de tiempo (UTC)¶
09:28 Release v2.4.0 desplegado (commit a91b...). CI en verde. Puerta de
evaluación pasada en 0.94 (sobre el umbral de 0.92). Periodo de bake
de 10 minutos, completado limpio.
09:31 Comienza la primera clase. ~28 estudiantes empiezan a enviar items
de quiz.
09:34 Pico en las líneas de log del portal "spanish_gloss=null" — pero no
se dispara ninguna alerta porque la regla de alerta estaba en errores
5xx del tutor, no en contenido de respuesta.
09:42 Primer ticket de soporte de estudiante: "el español está en blanco
en mi quiz".
09:47 Borja ve el ticket. Revisa el panel. Latencia normal; tasa de error
0%.
09:51 Borja lee manualmente el JSON de respuesta afectado. El campo
`spanish` es una string vacía en ~30% de las respuestas. Los logs
del tutor muestran que el campo se está fijando a "" en peticiones
donde la generación del modelo alcanzó el token EOS antes de
producir el gloss.
10:05 Decisión: rollback a v2.3.4 (último bueno conocido). Confirmado que
la imagen previa está en el registry (--keep 5 la preservó).
10:12 just rollback completa. Smoke test en verde.
10:18 La clase verifica que el gloss español ha vuelto. La clase continúa
con 6 minutos de retraso por la interrupción.
3. Factores contribuyentes¶
- Presupuesto de decodificación demasiado ajustado. v2.4.0 redujo
max_decode_tokensde 32 a 16 para mejorar la latencia p50. El cambio pasó CI porque las respuestas del conjunto de evaluación encajaban en 16 tokens, pero la distribución real tiene una cola larga de glosses españoles más largos (p. ej.,going to write→voy a escribir un email a mi familiaen algunas variantes prompteadas). - El conjunto de evaluación no incluía los prompts de cola larga. La evaluación del capstone (
phase-39-capstone.yaml) tiene 5-12 items; el conjunto de regresión tiene 300 items. Ninguno incluye los prompts que el módulo de quiz del portal genera en tiempo de ejecución. - El alerting era solo-5xx. Un 200-con-campo-vacío es un fallo de contenido que la taxonomía de alertas existente no cubría.
- Sin chequeo de forma de contenido en la puerta. La rúbrica de evaluación (theory 06) puntúa por eje pero no chequea por separado "el campo español no está vacío" como puerta bloqueante.
- El smoke del periodo de bake no incluía una muestra de inputs de cola larga. El smoke test del bake eran 10 prompts fijos; los 10 encajaban en 16 tokens; la regresión era invisible al smoke.
4. Acciones¶
| Acción | Dueño | Fecha | Hecho cuando |
|---|---|---|---|
Añadir max_decode_tokens a un config fijado por manifest; CI falla en caída > 4× |
Borja | 2026-08-21 | Test en tests/phase38/ que falla si max_decode_tokens cae por debajo de 24 |
| Muestrear 50 prompts de cola larga desde los logs del portal al conjunto de regresión | Borja | 2026-08-25 | tests/eval/regression_corpus.jsonl extendido; CI re-baselineado |
| Añadir alerta Prometheus: contador tutor_response_empty_field_total | Borja | 2026-08-21 | Contador exportado; regla de alerta se dispara en > 1% durante 5 min |
| Añadir "gloss español no vacío" como sub-chequeo bloqueante en la puerta de evaluación | Borja | 2026-08-22 | La puerta de evaluación falla cualquier release donde > 2% de respuestas tengan gloss vacío |
| Ampliar el smoke del periodo de bake para incluir 5 prompts del portal muestreados | Borja | 2026-08-23 | El smoke test referencia prompts reales; re-ejecutado en cada deploy |
Cada acción apunta a un factor específico. Cada una es binaria-chequeable. Ninguna apunta a una persona.
Qué fuerza la plantilla¶
Tres cosas, todas valiosas:
- El retraso de detección sale a la superficie. Desde la línea de tiempo: retraso de alerta = 0 (no se disparó alerta; el ticket de usuario fue la señal a +16 min). Retraso de mitigación = 41 min tras el primer fallo observable. Ese hueco es el conjunto de acciones.
- Los múltiples factores se mantienen visibles. Listar 5 factores contribuyentes previene "ya añadimos una alerta y listo" — cada factor recibe su propia acción.
- El encuadre sin culpas previene recurrencia. "Borja debería testear con más cuidado" no habría prevenido el siguiente incidente equivalente; "la puerta de evaluación no cubre prompts de cola larga" sí.
Cuándo escribir un postmortem¶
Para el currículo: siempre que un deploy requiriera un rollback, o siempre que un fallo de cara al aprendiz excediera 10 minutos. Baches menores van al diario diario.
Para un sistema de producción: la convención de la industria es "cualquier incidente con impacto medible al usuario". Elige una barra y mantenla; "no escribimos postmortems para los pequeños" es cómo se degrada la cultura.
El ejercicio /break¶
break/00-break-skip-postmortem.md simula saltarse el postmortem tras el incidente de arriba y muestra el mismo incidente recurriendo tres meses después con una regresión ligeramente distinta del presupuesto de decodificación. El coste es concreto: más minutos de interrupción de clase, más el meta-coste de "sabíamos que esto era un problema pero no actuamos sobre ello".
El arreglo al /break no es un cambio de código. Es un cambio de proceso: escribir el postmortem.
Referencia¶
- Google SRE, "Postmortem Culture: Learning from Failure" (capítulo 15 de Site Reliability Engineering, 2016). El encuadre sin culpas se origina aquí.
- Allspaw, "The Infinite Hows" (2014). Por qué los "5 porqués" son un mal encuadre; "factores contribuyentes" es mejor.
- Etsy, "Blameless PostMortems and a Just Culture" (2012). El marco cultural.
Siguiente: ../break/00-break-skip-postmortem.md para la demostración de recurrencia.