Módulo 5: Rollback And Incident Response

El runbook: los pasos que no se improvisan

Descripción

rollbackDecision() ya sabe decidir qué hacer: revertir, arreglar hacia adelante, o seguir. Pero saber qué hacer y ejecutarlo bien bajo presión son dos cosas distintas. Cuando el guardrail de recommendations se rompe a las tres de la mañana, la persona de guardia no tiene tiempo —ni debería necesitarlo— para pensar desde cero "¿a quién aviso primero? ¿confirmo el guardrail antes o después de apagar el flag? ¿qué le digo al resto del equipo?". Esta lección construye el runbook: la secuencia de pasos, escrita y acordada antes de que el incidente ocurra, que el equipo sigue sin improvisar.

Conexión con el módulo. El runbook de esta lección no inventa ningún paso nuevo — organiza en una secuencia fija lo que las lecciones 2 y 3 ya construyeron (el kill switch, rollbackDecision()) y lo que las lecciones 5, 6 y 7 todavía van a formalizar (mitigar antes de diagnosticar, la severidad, la comunicación). Este es el documento que junta todas esas piezas en el orden exacto en que se ejecutan durante un incidente real.

Una analogía: el checklist del piloto en emergencia

Un piloto comercial no reacciona a una emergencia —un motor que falla, una pérdida de presión en la cabina— pensando desde cero qué hacer. Reacciona sacando un checklist físico, impreso, con pasos numerados que se siguen en un orden exacto, sin saltarse ninguno aunque la memoria del piloto le diga que ya sabe lo que hay que hacer. Ese checklist no se escribió en el momento de la emergencia —se escribió con calma, en tierra, por gente que pensó cuidadosamente cada paso sin la presión de una cabina en alarma—. La razón no es que los pilotos no confíen en su propio criterio: es que el criterio de cualquier persona, bajo estrés real, a 10,000 metros de altura, es peor que el mismo criterio aplicado con calma, semanas antes.

El runbook de un incidente de software cumple exactamente esa función. No se escribe mientras el guardrail está roto y hay 25,000 usuarios expuestos — se escribe antes, con el equipo completo, pensando cada paso sin la presión del momento. Cuando el incidente llega, nadie improvisa el orden: alguien abre el runbook y lo sigue.

Ejemplo trabajado: runRunbook() sobre el incidente de recommendations

// runRunbook: ejecuta los pasos de un runbook PRE-ESCRITO, en orden, sobre el estado
// real del incidente -- nadie improvisa el orden ni se salta pasos a mitad de la crisis.
function runRunbook(steps, state) {
  let current = { ...state };
  const log = [];
  for (const step of steps) {
    const result = step.action(current);
    current = { ...current, ...result.nextState };
    log.push({ id: step.id, name: step.name, outcome: result.outcome });
  }
  return { log, finalState: current };
}

const recommendationsRunbook = [
  { id: 1, name: 'acknowledge', action: (s) => ({
    outcome: 'on-call reconoce la alerta del dashboard de guardrails (M4)',
    nextState: { acknowledged: true },
  }) },
  { id: 2, name: 'confirm guardrail', action: (s) => ({
    outcome: s.p95Latency > s.ceiling
      ? 'guardrail CONFIRMADO roto (' + s.p95Latency + 'ms > ' + s.ceiling + 'ms)'
      : 'guardrail dentro de rango -- no era una alerta real',
    nextState: { guardrailConfirmed: s.p95Latency > s.ceiling },
  }) },
  { id: 3, name: 'mitigate', action: (s) => ({
    outcome: s.guardrailConfirmed ? 'recommendationsFlag.enabled = false (kill switch, M2 L5)' : 'sin accion -- guardrail no confirmado',
    nextState: { mitigated: s.guardrailConfirmed },
  }) },
  { id: 4, name: 'verify', action: (s) => ({
    outcome: s.mitigated ? 'p95Latency vuelve a la linea base (~650ms) para todos los usuarios' : 'nada que verificar',
    nextState: { verified: s.mitigated },
  }) },
  { id: 5, name: 'communicate', action: (s) => ({
    outcome: s.verified ? 'status update enviado: mitigado, SEV2, investigando causa raiz' : 'sin update',
    nextState: { communicated: s.verified },
  }) },
];

const initialState = { p95Latency: 910, ceiling: 800 };
const { log } = runRunbook(recommendationsRunbook, initialState);

console.log('=== runbook de recommendations sobre el incidente de rollout 10% ===\n');
log.forEach((l) => console.log('paso ' + l.id + ' (' + l.name + '): ' + l.outcome));

Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:

=== runbook de recommendations sobre el incidente de rollout 10% ===

paso 1 (acknowledge): on-call reconoce la alerta del dashboard de guardrails (M4)
paso 2 (confirm guardrail): guardrail CONFIRMADO roto (910ms > 800ms)
paso 3 (mitigate): recommendationsFlag.enabled = false (kill switch, M2 L5)
paso 4 (verify): p95Latency vuelve a la linea base (~650ms) para todos los usuarios
paso 5 (communicate): status update enviado: mitigado, SEV2, investigando causa raiz

Fíjate en algo que el código deja ver con claridad: cada paso depende del estado que dejó el paso anterior, no de una lista fija de acciones desconectadas. El paso 3 (mitigate) solo apaga el kill switch si el paso 2 confirmó que el guardrail está roto de verdad — si el paso 2 hubiera determinado que la alerta era ruido (p95Latency dentro del rango), el paso 3 no habría hecho nada, y el runbook se habría detenido ahí, sin necesidad de un rollback que no hacía falta. Ese encadenamiento —cada paso construye sobre la confirmación del anterior, nunca actúa a ciegas— es exactamente lo que separa un runbook real de una simple lista de sugerencias.

Por qué el runbook tiene exactamente estos cinco pasos, en este orden

El orden no es casualidad, y cada paso tiene una razón de existir que las próximas lecciones profundizan:

1. ACKNOWLEDGE     alguien especifico toma la responsabilidad -- nadie asume que "otro ya lo vio"
2. CONFIRM          se verifica que el guardrail esta roto de verdad, no es ruido del dashboard
3. MITIGATE         se detiene el dano -- kill switch, la opcion mas rapida (leccion 5: por que va ANTES de diagnosticar)
4. VERIFY           se confirma que la mitigacion funciono -- no se asume, se mide
5. COMMUNICATE      se informa el estado -- severidad, que se hizo, que sigue (leccion 6 y 7)

Nota que diagnosticar la causa raíz de la latencia rota no aparece en ningún paso de este runbook. Eso no es un olvido — es la lección 5 completa, adelantada aquí en una frase: el runbook de respuesta a un incidente se detiene en "mitigado y comunicado", no en "arreglado de raíz". Investigar por qué la query se volvió lenta es un trabajo real e importante, pero no bloquea ni retrasa ninguno de estos cinco pasos — ocurre después, con la exposición ya controlada, sin la presión de usuarios afectados en ese momento.

Errores comunes

No tener ningún runbook escrito, y confiar en que "el equipo ya sabe qué hacer". Qué pasa: un incidente ocurre, y cada persona involucrada toma decisiones distintas sobre qué hacer primero —alguien intenta diagnosticar, otra persona intenta avisar a liderazgo, un tercero busca el botón del kill switch sin saber si ya se confirmó que el guardrail está realmente roto—. Por qué pasa: un equipo técnico competente confía, razonablemente, en su propio criterio individual — pero un incidente en vivo no es el mejor momento para que cinco criterios individuales converjan de forma espontánea en el mismo orden de acciones. Cómo detectarlo: si le pides a dos personas del equipo que describan, de memoria, los pasos exactos que seguirían ante el guardrail de recommendations roto, y describen órdenes distintos, no existe un runbook real — existe una expectativa implícita, que es lo mismo que no tener nada. Cómo corregirlo: escribe el runbook con el equipo completo, con calma, antes de que haga falta usarlo — exactamente como el checklist del piloto se escribe en tierra.

Escribir un runbook, pero sin pasos de verificación —asumir que "mitigar" y "verificar que la mitigación funcionó" son lo mismo—. Qué pasa: el equipo tiene un runbook con un paso de "apagar el flag", pero ningún paso después que confirme, con datos, que el guardrail volvió a estar dentro del rango — se asume que apagar la exposición automáticamente resuelve el problema, sin comprobarlo. Por qué pasa: la acción de mitigar (apagar el kill switch) se siente tan definitiva que parece redundante verificar su efecto después. Cómo detectarlo: si el runbook termina en el paso de mitigación, sin un paso posterior que revise el dashboard de guardrails, falta el paso 4 del ejemplo de esta lección. Cómo corregirlo: como muestra runRunbook(), el paso verify no es opcional — confirma con datos reales que la mitigación tuvo el efecto esperado, en vez de asumirlo por la lógica de que "debería haber funcionado".

Escribir un runbook tan detallado y rígido que nadie puede seguirlo bajo presión real. Qué pasa: alguien redacta un runbook de veinte pasos, con instrucciones extremadamente específicas para cada variante posible del incidente, y en la práctica nadie lo consulta durante una emergencia real porque es demasiado largo para leer mientras el sistema está afectado. Por qué pasa: es tentador anticipar cada caso posible por escrito, pensando que más detalle siempre es mejor. Cómo detectarlo: si el runbook tiene tantos pasos o ramas que nadie puede describir su estructura general de memoria, es probable que en un incidente real termine ignorándose. Cómo corregirlo: como el runbook de cinco pasos de esta lección, un runbook útil cabe en la cabeza del equipo — pocos pasos, cada uno con un propósito claro, y con espacio para el juicio humano dentro de cada paso, no un árbol de decisión imposible de memorizar.

Ejercicios

Ejercicio 1 — Simula el caso donde la alerta era ruido. Ejecuta mentalmente runRunbook() con initialState = { p95Latency: 720, ceiling: 800 } (una latencia que en realidad está por debajo del techo). ¿Qué cambia en la salida del paso 2 en adelante, y por qué el runbook "se detiene" de forma segura sin que nadie tenga que decidirlo manualmente?

Ver solución

En el paso 2, s.p95Latency > s.ceiling se evalúa como 720 > 800, que es false — así que guardrailConfirmed queda en false, y el outcome reportado es "guardrail dentro de rango -- no era una alerta real". En el paso 3, s.guardrailConfirmed es false, así que la condición del mitigate no se cumple, y el outcome es "sin accion -- guardrail no confirmado" — el kill switch nunca se activa. Los pasos 4 y 5 siguen la misma cadena: como mitigated nunca fue true, ambos reportan "nada que verificar" y "sin update". El runbook completo corre sus cinco pasos igual, pero cada uno, al revisar el estado dejado por el anterior, decide correctamente no actuar — nadie tuvo que interrumpir manualmente el proceso ni decidir "esto no era nada", el propio encadenamiento de estado lo maneja.

Ejercicio 2 — Agrega un sexto paso. El equipo de Mercado quiere agregar un paso escalate entre mitigate (paso 3) y verify (paso 4), que solo se active si la mitigación tomó más de 10 minutos en confirmarse (imagina que el estado trae un campo mitigationMinutes). Escribe, en JavaScript, el objeto de ese nuevo paso, siguiendo el mismo formato que los demás.

Ver solución
{ id: 3.5, name: 'escalate', action: (s) => ({
  outcome: s.mitigated && s.mitigationMinutes > 10
    ? 'escalado a tech lead -- la mitigacion tardo mas de lo esperado'
    : 'sin escalar -- mitigacion dentro del tiempo esperado',
  nextState: { escalated: s.mitigated && s.mitigationMinutes > 10 },
}) }

El paso sigue el mismo patrón que los demás: revisa el estado que dejaron los pasos anteriores (s.mitigated), agrega su propia condición (s.mitigationMinutes > 10), y actualiza el estado con nextState para que los pasos siguientes puedan usarlo. Este ejercicio anticipa una idea que la lección 6 desarrolla a fondo: la severidad y la escalación de un incidente no son estáticas — pueden cambiar según cómo se comporta la respuesta misma, no solo según el incidente original.

Ejercicio 3 — Explica el runbook sin usar la palabra "runbook". En dos o tres frases, explica a alguien nuevo en el equipo por qué Mercado tiene una secuencia de pasos escrita de antemano para responder al guardrail roto de recommendations, en vez de confiar en que cada persona de guardia decida sobre la marcha.

Ver solución

Un ejemplo de respuesta: "Es como el checklist que sigue un piloto en una emergencia: no lo escribe en el momento, con la presión de la situación — lo tiene preparado de antemano, con calma, y solo lo ejecuta cuando hace falta. Nosotros hicimos lo mismo con los pasos para responder cuando algo se rompe en recommendations: los pensamos con tiempo, sin la presión de un incidente real, para que a las tres de la mañana nadie tenga que decidir desde cero qué hacer primero." La idea central: la calidad de una decisión bajo presión depende de cuánto trabajo de pensamiento ya se hizo antes de que la presión existiera.

Resumen y siguiente paso

En esta lección construiste runRunbook(), la secuencia de cinco pasos —acknowledge, confirm guardrail, mitigate, verify, communicate— que ejecuta, en orden y con cada paso construyendo sobre el estado del anterior, la respuesta completa al incidente de recommendations. Viste que cada paso depende de la confirmación del anterior —el runbook no actúa a ciegas—, y que ninguno de los cinco pasos incluye diagnosticar la causa raíz: eso llega después, con la exposición ya controlada.

Antes de avanzar deberías poder: explicar por qué un runbook se escribe antes del incidente y no durante; describir la diferencia entre "mitigar" y "verificar que la mitigación funcionó" como dos pasos separados; y modificar runRunbook() para agregar un paso nuevo que dependa del estado dejado por los anteriores.

El runbook de esta lección menciona, de pasada, que diagnosticar la causa raíz no bloquea ninguno de los cinco pasos. La lección 5 convierte esa observación en el principio central de toda respuesta a incidentes: mitigar primero, diagnosticar después — y muestra, con números, cuánto cuesta invertir ese orden.

Recursos

  • PagerDuty Incident Response Documentation, "During an Incident" — response.pagerduty.com/during/during_an_incident. La referencia práctica de la industria sobre la estructura de pasos durante un incidente activo — muy cercana en espíritu a la secuencia de cinco pasos de esta lección. En inglés.
  • Google SRE Book, Capítulo 14, "Managing Incidents" — sre.google/sre-book/managing-incidents. Explica por qué los procesos estructurados y predefinidos superan a la improvisación individual, incluso entre ingenieros altamente capacitados, durante un incidente real. En inglés.
  • Atlassian, "How we respond to an incident" — atlassian.com/incident-management/handbook/incident-response. El manual público de Atlassian sobre su propio proceso de respuesta, con pasos escritos de antemano similares en estructura al runbook de esta lección. En inglés.