Módulo 5: Rollback And Incident Response
Mitigar primero, diagnosticar después
Descripción
El runbook de la lección 4 tenía un detalle que quedó sin explicar del todo: el paso mitigate (apagar el kill switch) viene antes que cualquier investigación sobre por qué la latencia se rompió. Esta lección nombra ese orden como un principio general de respuesta a incidentes, no solo una decisión particular del runbook de recommendations: mitiga primero, diagnostica después — nunca al revés, sin importar qué tan curioso o urgente se sienta entender la causa raíz en el momento.
Conexión con el módulo. Las lecciones 2 y 3 dieron la herramienta (el kill switch) y el criterio (rollbackDecision()) para mitigar rápido. La lección 4 mostró el orden dentro de un runbook concreto. Esta lección explica por qué ese orden importa, con números que muestran el costo real de invertirlo — antes de que la lección 6 hable de a quién avisar, y la lección 7 mida qué tan rápido se respondió en total.
Una analogía: apagar el fuego antes de investigar por qué empezó
Un incendio en una cocina no espera a que alguien determine si empezó por un cable pelado, una sartén con demasiado aceite, o una estufa mal calibrada. La primera reacción, la que cualquier entrenamiento de seguridad enseña, es apagarlo — con un extintor, cortando el gas, lo que corresponda según el tipo de fuego —, no investigar su origen mientras las llamas siguen creciendo. La investigación sobre la causa —revisar la instalación eléctrica, examinar la sartén, calibrar la estufa— llega después, con el fuego ya apagado, sin nadie respirando humo mientras tanto.
Diagnosticar la causa raíz de un guardrail roto, con el guardrail todavía roto y usuarios reales todavía expuestos, es investigar el origen del incendio mientras el fuego sigue ardiendo. Se puede hacer — pero cada minuto que pasa investigando en vez de apagando es un minuto más de daño real, acumulándose sobre gente real, mientras la curiosidad legítima sobre "qué pasó exactamente" compite con la urgencia de que deje de pasar.
Ejemplo trabajado: el costo real de invertir el orden
// simulateResponse: compara el ORDEN de las acciones -- diagnosticar primero vs
// mitigar primero -- sobre el mismo incidente de recommendations. La mitigacion en si
// es identica en los dos ordenes (apagar el kill switch); lo que cambia es CUANDO
// ocurre dentro de la secuencia.
function timeToMitigateFor(steps) {
let elapsed = 0;
for (const step of steps) {
elapsed += step.minutes;
if (step.isMitigation) return elapsed;
}
return null;
}
function totalElapsed(steps) {
return steps.reduce((sum, s) => sum + s.minutes, 0);
}
const orders = {
diagnoseFirst: [
{ action: 'leer logs y dashboards buscando la causa raiz', minutes: 35 },
{ action: 'confirmar hipotesis: query sin index en el join de recommendations', minutes: 40 },
{ action: 'recien ahora, apagar el kill switch', minutes: 2, isMitigation: true },
],
mitigateFirst: [
{ action: 'confirmar que el guardrail esta roto (paso 2 del runbook)', minutes: 3 },
{ action: 'apagar el kill switch de inmediato', minutes: 2, isMitigation: true },
{ action: 'con el trafico ya protegido, ahora si buscar la causa raiz', minutes: 75 },
],
};
console.log('=== las mismas acciones, dos ordenes distintos ===\n');
Object.keys(orders).forEach((order) => {
console.log(order + ':');
let elapsed = 0;
orders[order].forEach((s) => {
elapsed += s.minutes;
console.log(' ' + s.action + ' (t=' + elapsed + 'min)' + (s.isMitigation ? ' <- MITIGADO AQUI' : ''));
});
console.log('');
});
console.log('=== el costo real del orden ===\n');
Object.keys(orders).forEach((order) => {
const ttm = timeToMitigateFor(orders[order]);
const total = totalElapsed(orders[order]);
console.log(order.padEnd(14) + 'timeToMitigate=' + String(ttm).padStart(3) + 'min tiempo total hasta causa raiz=' + total + 'min');
});
const diff = timeToMitigateFor(orders.diagnoseFirst) - timeToMitigateFor(orders.mitigateFirst);
console.log('\nDiferencia en timeToMitigate: ' + diff + 'min mas de usuarios expuestos a la latencia rota, solo por el orden de las acciones.');
Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:
=== las mismas acciones, dos ordenes distintos ===
diagnoseFirst:
leer logs y dashboards buscando la causa raiz (t=35min)
confirmar hipotesis: query sin index en el join de recommendations (t=75min)
recien ahora, apagar el kill switch (t=77min) <- MITIGADO AQUI
mitigateFirst:
confirmar que el guardrail esta roto (paso 2 del runbook) (t=3min)
apagar el kill switch de inmediato (t=5min) <- MITIGADO AQUI
con el trafico ya protegido, ahora si buscar la causa raiz (t=80min)
=== el costo real del orden ===
diagnoseFirst timeToMitigate= 77min tiempo total hasta causa raiz=77min
mitigateFirst timeToMitigate= 5min tiempo total hasta causa raiz=80min
Diferencia en timeToMitigate: 72min mas de usuarios expuestos a la latencia rota, solo por el orden de las acciones.
Este resultado es, quizás, el más importante de todo el módulo, y vale la pena leerlo con cuidado. Las mismas tres acciones ocurren en los dos órdenes —confirmar, mitigar, investigar—; lo único que cambia es en qué posición de la secuencia cae la mitigación. Con diagnoseFirst, el kill switch no se activa hasta el minuto 77 — 25,000 usuarios siguieron expuestos a la latencia rota durante más de una hora, mientras el equipo investigaba. Con mitigateFirst, el kill switch se activa en el minuto 5 — el daño se detiene casi 15 veces más rápido.
Y fíjate en el segundo número, el que casi nadie espera: el tiempo total hasta encontrar la causa raíz es prácticamente el mismo en los dos órdenes —77 minutos contra 80—. Mitigar primero no hizo que investigar la causa raíz tomara mucho más tiempo (80 contra 77, una diferencia de apenas 3 minutos, el tiempo del paso de confirmación). Lo que sí cambió, y radicalmente, fue cuánto tiempo estuvieron los usuarios expuestos mientras esa investigación ocurría. Ese es exactamente el argumento a favor de mitigar primero: casi no cuesta nada en velocidad de diagnóstico, y ahorra una cantidad enorme de exposición real.
Por qué "mitigar primero" no es lo mismo que "no investigar"
Es fácil malinterpretar este principio como "no importa la causa raíz" — y eso sería un error tan grave como el que esta lección corrige. Mira de nuevo el orden mitigateFirst: el tercer paso, "buscar la causa raíz", sigue ahí, y sigue siendo necesario — nadie se queda satisfecho con "ya apagamos el flag, no hace falta entender qué pasó". La diferencia no es si se investiga, sino cuándo: con el daño ya detenido, o mientras el daño sigue ocurriendo. Investigar con calma, después de mitigar, tiene además una ventaja que el ejemplo no muestra explícitamente pero vale la pena nombrar: la investigación misma suele ser mejor cuando no hay presión de "cada minuto que pasa afecta a más gente" empujando a apresurar conclusiones — la misma prisa que puede llevar a un diagnóstico apurado y equivocado.
Este principio, además, es exactamente lo que justificó el orden del runbook de la lección 4: el paso mitigate viene en la posición 3, antes de cualquier paso de investigación — no porque investigar no importe, sino porque el orden correcto siempre resuelve primero el daño en curso, y solo después se dedica tiempo a entender por qué ocurrió.
Errores comunes
Diagnosticar la causa raíz con el sitio caído, en vez de mitigar primero. Qué pasa: frente a un guardrail roto, alguien del equipo empieza directamente a revisar logs, ejecutar queries de diagnóstico, o probar hipótesis sobre la causa —mientras la exposición sigue activa y afectando usuarios reales—, postergando la mitigación hasta tener una explicación clara. Por qué pasa: entender el problema se siente como el trabajo "real" de un ingeniero, y apagar algo sin saber exactamente por qué se rompió puede sentirse, incorrectamente, como una solución incompleta o poco rigurosa. Cómo detectarlo: si en algún momento de un incidente activo alguien dice "espera, déjame entender esto primero" antes de haber mitigado, ese es el error ocurriendo en tiempo real. Cómo corregirlo: como muestra el contraste de esta lección —77 minutos contra 5—, mitigar primero cuesta casi nada en velocidad de diagnóstico y ahorra una cantidad enorme de exposición; el orden correcto es mitigar, y después investigar con la misma o mayor profundidad.
Confundir "mitigar primero" con "no hace falta investigar la causa raíz". Qué pasa: el equipo apaga recommendations con el kill switch, respira aliviado, y nadie vuelve a tocar el tema de por qué la latencia se rompió en primer lugar — como si mitigar fuera equivalente a resolver. Por qué pasa: el alivio inmediato de ver el guardrail volver a la normalidad puede sentirse como el final del trabajo, cuando en realidad es solo el final de la primera mitad. Cómo detectarlo: si después de mitigar nadie tiene asignada la tarea de investigar la causa raíz, el problema de fondo sigue exactamente donde estaba —solo que sin usuarios expuestos por ahora—, listo para repetirse la próxima vez que alguien vuelva a subir el rollout. Cómo corregirlo: el paso mitigate del runbook nunca fue el último paso — la investigación sigue siendo necesaria, solo que ocurre después, sin la presión de un incidente activo.
Medir el éxito de la respuesta solo por qué tan rápido se encontró la causa raíz, sin medir cuánto tiempo estuvo la exposición activa. Qué pasa: al revisar cómo fue la respuesta a un incidente pasado, el equipo se felicita porque "encontramos el problema en menos de una hora" —el tiempo total hasta el diagnóstico—, sin notar ni reportar cuánto de ese tiempo los usuarios estuvieron expuestos al problema sin mitigación. Por qué pasa: el tiempo hasta el diagnóstico es más fácil de contar como una historia de éxito técnico que el tiempo de exposición, que suena más incómodo de reportar. Cómo detectarlo: si el reporte de un incidente menciona "encontramos la causa en X minutos" pero no menciona por separado "mitigamos en Y minutos", falta la métrica que de verdad le importa a los usuarios afectados. Cómo corregirlo: como en la salida de este ejemplo, siempre reporta las dos cifras por separado —timeToMitigate y el tiempo total hasta el diagnóstico completo—; son preguntas distintas, y la primera casi siempre importa más para la gente que estuvo expuesta.
Ejercicios
Ejercicio 1 — Cambia el costo de confirmar. En el orden mitigateFirst, el primer paso ("confirmar que el guardrail está roto") toma 3 minutos. Si ese paso de confirmación tomara 20 minutos en vez de 3 —imagina que el dashboard tarda en actualizarse—, ¿cuál sería el nuevo timeToMitigate para mitigateFirst? ¿Seguiría siendo mucho mejor que diagnoseFirst?
Ver solución
El nuevo timeToMitigate sería 20 + 2 = 22 minutos (el paso de confirmación más el de mitigación, el único paso marcado con isMitigation: true). Comparado con los 77 minutos de diagnoseFirst, 22 minutos sigue siendo dramáticamente mejor —menos de un tercio del tiempo—, aunque ya no tan extremo como el contraste original de 5 contra 77. Esto muestra algo importante: el principio de "mitigar primero" sigue siendo válido incluso si el paso de confirmación se vuelve más lento — lo que cambiaría, en ese caso, no es si mitigar primero sigue siendo mejor, sino cuánta ventaja da exactamente. Vale la pena, además, investigar por separado por qué el dashboard tarda tanto en confirmar un guardrail roto — eso también es una mejora real, aunque distinta de la decisión de ordenar los pasos.
Ejercicio 2 — Encuentra el caso donde diagnosticar primero sí tendría sentido. Piensa (no hace falta código) en una situación hipotética donde mitigar antes de entender el problema podría, en sí mismo, causar más daño que esperar un poco para diagnosticar. Descríbela en 2-3 frases.
Ver solución
Una situación posible: si la "mitigación" disponible no fuera un kill switch de bajo riesgo como el de recommendations, sino una acción con su propio costo alto o riesgoso —por ejemplo, reiniciar una base de datos completa sin saber si eso podría causar pérdida de datos en alguna transacción a medio procesar—. En ese caso, "mitigar primero" sin ningún diagnóstico podría cambiar un problema de latencia por un problema mucho peor. La lección clave no cambia: sigue siendo cierto que el diagnóstico completo debería posponerse tanto como sea razonablemente posible — pero "mitigar primero" asume que la mitigación disponible es de bajo riesgo, como el kill switch de un flag. Cuando la propia mitigación es riesgosa, hace falta al menos un diagnóstico mínimo —no el diagnóstico completo de la causa raíz, pero sí lo suficiente para saber que la mitigación no va a empeorar las cosas—.
Ejercicio 3 — Explica el principio usando la analogía del incendio. En dos o tres frases, explica a alguien que nunca trabajó en software por qué un equipo de ingeniería apaga primero una funcionalidad rota y solo después investiga por qué se rompió, usando la analogía del fuego en la cocina.
Ver solución
Un ejemplo de respuesta: "Cuando hay un incendio en la cocina, primero se apaga el fuego —con un extintor, cortando el gas— y recién después se investiga si fue un cable pelado o una sartén con mucho aceite. Nadie se queda investigando la causa mientras las llamas siguen creciendo. Nosotros hacemos lo mismo: cuando algo se rompe para nuestros usuarios, primero lo apagamos —para que deje de afectarles— y recién después, con calma y sin que nadie más salga perjudicado mientras tanto, investigamos exactamente qué lo causó." La idea central: el orden protege a la gente afectada primero, sin sacrificar casi nada en la calidad ni en la velocidad de la investigación posterior.
Resumen y siguiente paso
En esta lección nombraste y midiste el principio central de toda respuesta a incidentes: mitigar primero, diagnosticar después. Con timeToMitigateFor() comparaste dos órdenes de las mismas acciones sobre el mismo incidente de recommendations —diagnoseFirst mitigó recién en el minuto 77, mitigateFirst mitigó en el minuto 5— y viste que esa diferencia de 72 minutos de exposición evitada costó casi nada en velocidad de diagnóstico (80 minutos contra 77).
Antes de avanzar deberías poder: explicar por qué mitigar primero no significa "no investigar la causa raíz"; distinguir timeToMitigate del tiempo total hasta el diagnóstico completo, como dos métricas separadas; y nombrar una situación donde la mitigación disponible sea, en sí misma, lo suficientemente riesgosa como para justificar un diagnóstico mínimo antes de actuar.
Ya sabes qué hacer (rollbackDecision()), cómo ejecutarlo sin improvisar (el runbook), y en qué orden (mitigar antes de diagnosticar). La lección 6 agrega la pieza organizacional que falta: no todos los incidentes son iguales — algunos ameritan despertar a todo el equipo de guardia, y otros pueden esperar hasta el siguiente día hábil. ¿Cómo se decide eso, y quién se entera de cada tipo?
Recursos
- Google SRE Book, Capítulo 13, "Emergency Response" — sre.google/sre-book/emergency-response. Documenta casos reales de Google donde actuar rápido para contener el daño, antes de entender la causa completa, marcó la diferencia entre un incidente controlado y uno mucho más grave. En inglés.
- PagerDuty Incident Response Documentation, "What is an Incident?" — response.pagerduty.com/before/what_is_an_incident. Establece la distinción entre contener el impacto de un incidente y resolver su causa raíz como dos fases distintas del proceso — la base formal del principio de esta lección. En inglés.
- Charity Majors, "Deploys Are the WRONG Way to Change User Experience" — honeycomb.io/blog/deploys-wrong-way-change-user-experience. Refuerza, desde la perspectiva de feature flags, por qué contener el impacto (apagar el flag) debe separarse de investigar el código que causó el problema. En inglés.