La mayoría de los post-mortems que se publican son inventados para ilustrar un formato. Este no: es un incidente real del flujo de deploy de este mismo sitio, ocurrido mientras lo construía. No fue producción —no hubo usuarios afectados— pero un post-mortem no se mide por el tamaño del incidente, sino por el método. Y el método es el mismo a cualquier escala. Si querés el formato en detalle, lo desgloso en la anatomía de un post-mortem blameless; acá lo aplico a un caso concreto.

Reliability no empieza en producción. Empieza en el flujo con el que shippeás: si ahí hay fricción, se traslada a todo lo demás.

Contexto: ¿qué estaba pasando?

Estaba iterando sobre el sitio con el dev server (next dev) corriendo para previsualizar cambios en vivo. Para validar tipos antes de seguir, corrí en paralelo un next build (build de producción) sin apagar el dev server.

Detección: ¿qué alerta saltó?

No saltó una alerta automática — y esa ya es la primera lección. La "alerta" fue manual: el preview empezó a devolver errores y un chequeo de salud al server falló. En los logs del build apareció el síntoma:

Error [PageNotFoundError]: Cannot find module for page: /blog/[slug]
Error [PageNotFoundError]: Cannot find module for page: /blog

Timeline (minuto a minuto)

  • T+0 — Corro rm -rf .next && next build con next dev todavía activo.
  • T+0:30 — El build de producción reescribe .next, el directorio que el dev server estaba usando en vivo.
  • T+1 — El preview empieza a servir páginas rotas: PageNotFoundError. Un fetch al server falla.
  • T+2 — Diagnóstico: reviso procesos (ps, lsof). El server está escuchando el puerto — no está caído, está sirviendo sobre un .next corrupto. Eso descarta "se murió el proceso" y apunta al estado compartido.
  • T+3 — Confirmo la causa: un next build corrió sobre el .next de un next dev vivo.
  • T+3:30 — Fix: stop del dev server → rm -rf .next → reinicio limpio.
  • T+4 — Preview sirviendo OK de nuevo (200 en todas las rutas).

Impacto: ¿a quién afectó?

Honestamente: cero usuarios de producción. Fue el entorno de desarrollo. Pero el impacto real fue de flujo: bloqueó la verificación de cambios por unos minutos y, peor, el preview mostraba páginas rotas que podían confundirse con un bug del código que estaba escribiendo. En un contexto de CI sin un chequeo posterior, el mismo patrón podría dejar pasar un artefacto roto.

Root cause: ¿cuál fue la causa raíz?

No fue "me equivoqué de comando". Eso es el síntoma. La causa raíz es de diseño del flujo: next dev y next build comparten el mismo directorio de estado (.next) sin ningún aislamiento ni guard. Nada impedía que un build de producción pisara el estado de un dev server en vivo. La culpa no es de la persona que corrió el comando; es del flujo que permitía la colisión en silencio.

Fix inmediato

Parar el dev server, borrar el .next contaminado y reiniciar:

# stop del dev server, luego:
rm -rf .next
npm run dev

Fix permanente: ¿qué cambié para que no vuelva a pasar?

  • Regla de proceso: no correr next build con el dev server activo. El next dev ya valida TypeScript al compilar cada ruta, así que el build de producción se reserva para cuando dev está apagado.
  • Aislamiento cuando hace falta validar en paralelo: un directorio de salida distinto (next build con un distDir separado) elimina la colisión de raíz — el equivalente a no compartir estado mutable entre dos procesos.
  • El gate va en CI, no a mano: la validación de tipos y el build de producción viven en el pipeline (GitHub Actions), aislados del entorno local. Validar a mano "por las dudas" fue lo que abrió la puerta al incidente.

Qué cambié en observability para detectarlo antes

La detección fue manual y tardía. Lo accionable: un smoke test después de cualquier build — un simple fetch a las rutas clave que devuelva el status. Es exactamente la etapa que el pipeline ya corre (Lighthouse CI carga las páginas reales antes de aprobar). La lección: si un humano es el primero en notar que algo está roto, falta una señal automática.

La lección de growth

Este fue un incidente chico, de entorno de desarrollo. Pero el principio escala: downtime no es solo un SLA roto, es conversión perdida — y la fricción en el flujo de deploy es velocidad de iteración perdida. Por eso trato la reliability, incluso la de mi propio tooling, como trabajo de growth: cada minuto que el sistema no está confiable es un experimento que no corrés, un cambio que no shippeás. Custodiar el crecimiento técnico de un producto es, en buena parte, hacer que estos incidentes sean aburridos: detectados rápido, entendidos a fondo, e imposibles de repetir.

¿Querés a alguien que opere tu producto con esta cabeza —que cuando algo falle lo entienda y lo cierre, en vez de solo apagar el fuego? Hablemos.