Módulo 1: From Experiment To Launch
Proyecto: decide el enfoque de lanzamiento de `recommendations`
Descripción
Las siete lecciones de este módulo construyeron, pieza por pieza, el marco completo: la brecha entre "ganó" y "listo para todos" (L2), el lanzamiento como decisión de riesgo (L3), el radio de impacto medido en personas reales (L4), la separación entre deploy y release que hace posible controlarlo (L5), las tres propiedades de un lanzamiento cuidadoso (L6), y el veredicto de alto nivel sobre recommendations (L7). Este mini-proyecto te pone en la posición exacta del equipo de Mercado hoy: con el veredicto de la lección 7 ya en la mano —"lanzar empezando chico"—, alguien tiene que convertir eso en una recomendación concreta y defendible, comparando el radio de impacto real de las opciones disponibles, con números, no con la intuición de quién habló más fuerte en la reunión.
Conexión con el módulo. Este proyecto no introduce ningún concepto nuevo: reutiliza blastRadius() exactamente como quedó en la lección 4, y le agrega una capa de decisión explícita —decideLaunchApproach()— que compara candidatos contra un límite de exposición aceptable. Es, literal, el ensayo del trabajo real que este módulo entero preparó: no memorizar que "el canary es mejor" en abstracto, sino saber demostrarlo con números sobre el caso concreto, y usar esos números para justificar una decisión ante quien te la cuestione. Este proyecto decide el enfoque — arrancar chico o no—; el cómo técnico de subir de 1% a 100% sin repetir este riesgo es el trabajo de los módulos 2 a 5, que siguen después de este.
Una analogía: el ingeniero de demoliciones, con el plano de la manzana en la mano
Vuelve a la analogía de la lección 4: el ingeniero de demoliciones no solo sabe que existe un radio de impacto — tiene, antes de detonar nada, un plano de la manzana con las distancias exactas a cada edificio vecino, y una regla clara: "nadie a menos de X metros del punto de detonación". Con ese plano y esa regla, la decisión de cuánto evacuar deja de ser una corazonada ("evacuemos bastante, por si acaso") y se vuelve una comparación verificable: esta distancia cumple la regla, esa otra no. Este proyecto es exactamente ese plano, aplicado a recommendations: en vez de un radio en metros, un radio en usuarios afectados; en vez de una regla de distancia mínima, un límite explícito de cuánta gente Mercado está dispuesta a exponer a una regresión ya conocida, mientras arma el resto del plan.
La decisión completa, paso a paso
Parte 1 — El límite que Mercado está dispuesto a aceptar
Antes de comparar opciones, el equipo de Mercado fija un número concreto: cuántas personas está dispuesto a exponer a la regresión de latencia conocida —no cero, porque cero significaría no lanzar nunca nada nuevo, pero tampoco los miles que resultarían de un lanzamiento sin cuidado— mientras arma el resto del plan de lanzamiento (flags, rollout, monitoreo, rollback de los módulos 2 a 5). El equipo decide: no más de 200 usuarios afectados durante esta primera etapa de contención.
Parte 2 — Comparar los dos enfoques con blastRadius() y decideLaunchApproach()
Reutilizamos blastRadius() exactamente como quedó en la lección 4, sin ningún cambio, y le agregamos una función de decisión que compara candidatos contra el límite de la Parte 1:
// PROYECTO: decidir el enfoque de lanzamiento de recommendations en Mercado,
// dado que el A/B gano (checkout conversion +18.75%, p=0.0114, significativo)
// pero rompio el guardrail de latencia conocido (p95 650ms -> 910ms, techo 800ms).
// Reutiliza blastRadius() exactamente como quedo en la leccion 4, sin cambios.
function blastRadius({ totalUsers, rolloutPercent, regressionRate }) {
const exposedUsers = Math.round(totalUsers * rolloutPercent);
const affectedUsers = Math.round(exposedUsers * regressionRate);
return { rolloutPercent, exposedUsers, affectedUsers };
}
// decideLaunchApproach: compara candidatos de lanzamiento contra un limite de
// exposicion que el equipo considera aceptable para una regresion YA conocida,
// mientras arma el plan completo de flags/rollout/monitoreo/rollback (M2-M5).
function decideLaunchApproach({ totalUsers, regressionRate, maxAcceptableExposure, candidates }) {
return candidates.map((c) => {
const { exposedUsers, affectedUsers } = blastRadius({ totalUsers, rolloutPercent: c.rolloutPercent, regressionRate });
const verdict = affectedUsers <= maxAcceptableExposure
? 'CONTIENE el dano dentro del limite aceptable'
: 'EXCEDE el limite aceptable de exposicion';
return { ...c, exposedUsers, affectedUsers, verdict };
});
}
const totalUsers = 250000; // base de compradores alcanzable por recommendations
const regressionRate = 0.05; // fraccion de expuestos que sufre la regresion de latencia conocida
const maxAcceptableExposure = 200; // el equipo de Mercado decide: no mas de 200 personas expuestas a una regresion YA conocida mientras arma el plan completo
const candidates = [
{ name: 'ship a 100% de golpe', rolloutPercent: 1.0 },
{ name: 'empezar con canary 1%', rolloutPercent: 0.01 },
];
console.log('=== decideLaunchApproach: lanzamiento de recommendations en Mercado ===\n');
console.log('Contexto: A/B gano (checkout conversion +18.75%, p=0.0114) pero el guardrail de latencia');
console.log('esta roto (p95 910ms, techo 800ms). maxAcceptableExposure = ' + maxAcceptableExposure + ' usuarios.\n');
const decision = decideLaunchApproach({ totalUsers, regressionRate, maxAcceptableExposure, candidates });
decision.forEach((d) => {
console.log(d.name.padEnd(24) + 'exposedUsers=' + d.exposedUsers.toLocaleString('en-US').padStart(7) +
' affectedUsers=' + String(d.affectedUsers).padStart(6) + ' -> ' + d.verdict);
});
const chosen = decision.find((d) => d.verdict.startsWith('CONTIENE'));
console.log('\nEnfoque elegido: "' + chosen.name + '" (' + chosen.affectedUsers + ' usuarios afectados, dentro del limite de ' + maxAcceptableExposure + ').');
console.log('La mecanica para llegar de 1% a 100% sin repetir este riesgo -- flags, rampa, monitoreo, rollback -- es el trabajo de M2 a M5.');
Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:
=== decideLaunchApproach: lanzamiento de recommendations en Mercado ===
Contexto: A/B gano (checkout conversion +18.75%, p=0.0114) pero el guardrail de latencia
esta roto (p95 910ms, techo 800ms). maxAcceptableExposure = 200 usuarios.
ship a 100% de golpe exposedUsers=250,000 affectedUsers= 12500 -> EXCEDE el limite aceptable de exposicion
empezar con canary 1% exposedUsers= 2,500 affectedUsers= 125 -> CONTIENE el dano dentro del limite aceptable
Enfoque elegido: "empezar con canary 1%" (125 usuarios afectados, dentro del limite de 200).
La mecanica para llegar de 1% a 100% sin repetir este riesgo -- flags, rampa, monitoreo, rollback -- es el trabajo de M2 a M5.
La decisión, en limpio
| Enfoque | Usuarios expuestos | Usuarios afectados | ¿Dentro del límite (200)? |
|---|---|---|---|
| Ship a 100% de golpe | 250,000 | 12,500 | No — 62.5 veces el límite |
| Canary al 1% | 2,500 | 125 | Sí — 37.5% del límite, con margen |
El "ship a 100% de golpe" no solo excede el límite: lo excede por un factor de 62.5 — 12,500 afectados contra un tope de 200. No es un caso límite discutible; es, con los números sobre la mesa, una opción claramente descartada. El canary al 1%, en cambio, deja 125 usuarios afectados dentro de un límite de 200 — con margen suficiente como para que, si la primera etapa del rollout gradual (módulo 3) sube un poco más allá del 1% exacto, el equipo siga dentro de lo aceptable antes de la siguiente revisión.
Vale la pena notar lo que esta decisión no resuelve todavía: no dice si la siguiente etapa después del 1% es 5%, 10% o 20% — eso depende de qué tan rápido el equipo pueda confirmar que el guardrail se mantiene contenido en la etapa actual, la mecánica exacta del módulo 3. No dice qué pasa si, incluso al 1%, el guardrail empeora más de lo esperado — esa decisión de frenar o revertir es el módulo 4 y 5. Lo que sí resuelve, con el mismo rigor numérico que el resto de esta guía exige, es la pregunta que corresponde al módulo 1: de las dos formas extremas de empezar, cuál es la que un equipo con criterio elegiría, y por qué, en números.
Errores comunes
Fijar maxAcceptableExposure después de ver los resultados, para justificar la conclusión que ya se quería. Qué pasa: alguien, al ver que el canary da 125 afectados, propone retroactivamente un límite de 100 para poder decir "ni siquiera el canary es aceptable, mejor no lancemos nada" — o al revés, sube el límite a 15,000 para poder justificar el ship a 100%. Por qué pasa: es tentador ajustar el criterio a la conclusión deseada, en vez de fijar el criterio antes de ver el resultado. Cómo detectarlo: el número de maxAcceptableExposure cambia después de correr decideLaunchApproach(), sin ninguna razón de negocio nueva que lo justifique. Cómo corregirlo: como en la Parte 1 de este proyecto, fija el límite antes de comparar candidatos, con una razón de negocio explícita (aquí: "cuánta gente estamos dispuestos a exponer a un problema ya conocido mientras armamos el resto del plan") — no como una conclusión disfrazada de dato de entrada.
Tratar "el canary contiene el daño" como si fuera lo mismo que "el problema está resuelto". Qué pasa: al ver el veredicto 'CONTIENE el dano dentro del limite aceptable', alguien concluye que ya no hace falta seguir trabajando en el guardrail de latencia, porque "de todos modos está contenido". Por qué pasa: la palabra "contiene" suena a solución, cuando en realidad describe solo el tamaño del daño mientras el problema sigue sin arreglarse. Cómo detectarlo: después de elegir el canary, nadie en el equipo tiene asignado seguir trabajando en la causa raíz de la regresión de latencia. Cómo corregirlo: recuerda el punto de la lección 4 — contener el radio de impacto y arreglar el problema son dos acciones independientes. Elegir el canary le compra tiempo al equipo para arreglar la latencia con solo 125 personas afectadas mientras tanto, no reemplaza la necesidad de arreglarla.
Presentar solo el enfoque elegido, sin mostrar la comparación completa. Qué pasa: al reportar la decisión hacia arriba, alguien dice solo "vamos a lanzar con un canary del 1%" sin mostrar los números de la alternativa descartada, dejando la decisión sin el respaldo que la hace defendible. Por qué pasa: mostrar solo la conclusión parece más limpio y directo que mostrar la comparación completa. Cómo detectarlo: si alguien pregunta "¿por qué no simplemente lo lanzamos a todos?", no hay una respuesta con números lista de inmediato. Cómo corregirlo: como en la tabla de "La decisión, en limpio" de este proyecto, siempre presenta las dos filas —la opción descartada y la elegida—, con el factor de diferencia (62.5x en este caso) explícito. Es exactamente lo que vuelve una decisión defendible ante cualquier pregunta.
Ejercicios de transferencia
A diferencia de los ejercicios de las lecciones anteriores, este proyecto te pide aplicar el método completo a un caso que esta guía nunca vio — la prueba real de si aprendiste el razonamiento o solo memoraste el resultado de recommendations.
Ejercicio 1 — Aplica el método a un límite de exposición distinto. El director de producto de Mercado, más conservador, propone maxAcceptableExposure: 50 en vez de 200. Corre (mentalmente o en Node) decideLaunchApproach() con ese nuevo límite, manteniendo el resto de los datos igual. ¿Cambia el enfoque elegido? ¿Qué tendría que ajustar el equipo para cumplir con ese límite más estricto?
Ver solución
Con maxAcceptableExposure: 50, el canary al 1% (125 afectados) ya no cumple — 125 > 50—, así que ninguno de los dos candidatos originales pasa el filtro. El equipo tendría que proponer un tercer candidato con un porcentaje de rollout todavía menor: usando blastRadius(), un rollout de 0.4% (0.004) da exposedUsers = round(250000 * 0.004) = 1,000 y affectedUsers = round(1000 * 0.05) = 50 — justo en el límite—. Esto demuestra que el modelo no está atado al 1% como un número mágico: el porcentaje correcto de arranque depende enteramente de qué tan estricto sea el maxAcceptableExposure que el negocio defina, y blastRadius() te permite encontrar el porcentaje exacto que cumple cualquier límite dado.
Ejercicio 2 — Aplica el método a un caso completamente nuevo. El equipo de logística de Mercado quiere lanzar un cambio en el algoritmo que calcula tiempos de entrega estimados. Un experimento previo mostró que el nuevo algoritmo reduce las quejas por "el pedido llegó más tarde de lo prometido" en un 22% (significativo) — pero también reveló que, para pedidos con múltiples paquetes, el cálculo se equivoca por más de un día en el 3% de los casos. La base alcanzable son 80,000 pedidos por semana. El equipo de logística fija maxAcceptableExposure: 60 pedidos afectados por semana durante la etapa de contención. Compara "lanzar a 100%" contra "canary al 2%" con decideLaunchApproach(), y decide el enfoque.
Ver solución
Con totalUsers: 80000, regressionRate: 0.03 (el 3% de error en pedidos multi-paquete): 100% de golpe → exposedUsers = 80,000, affectedUsers = round(80000 * 0.03) = 2,400 → 2,400 > 60 → EXCEDE. Canary al 2% → exposedUsers = round(80000 * 0.02) = 1,600, affectedUsers = round(1600 * 0.03) = 48 → 48 <= 60 → CONTIENE. El enfoque elegido es el canary al 2%, con 48 pedidos afectados dentro del límite de 60 — con menos margen que el caso de recommendations (125 de 200, un 62.5% del límite, contra 48 de 60, un 80% del límite), lo que sugiere que, si el equipo de logística quiere más margen de seguridad antes de la siguiente etapa, valdría la pena considerar un porcentaje de arranque todavía menor, como se vio en el ejercicio 1.
Ejercicio 3 — Defiende la decisión por escrito. Escribe el mensaje (100-150 palabras) que le enviarías al equipo ejecutivo de Mercado, recomendando el canary al 1% sobre el ship a 100%, usando los números exactos de este proyecto. Incluye: el resultado del experimento, el guardrail roto, la comparación numérica de las dos opciones, y qué sigue después de esta decisión (sin explicar todavía la mecánica de los módulos 2 a 5, solo nombrando que existe).
Ver solución
Un mensaje posible: "recommendations ganó el experimento con un 18.75% más de conversión en checkout (p=0.0114, estadísticamente significativo). Al mismo tiempo, confirmamos que introduce una regresión real en la latencia del checkout, por encima de nuestro límite aceptable. En vez de elegir entre lanzarlo a todos de inmediato o cancelarlo, comparamos el impacto de dos enfoques: lanzarlo al 100% expondría a 12,500 usuarios a esta regresión — muy por encima de las 200 personas que consideramos un límite razonable mientras resolvemos el problema de fondo. Empezar con un canary del 1% deja esa exposición en solo 125 personas, dentro de nuestro límite, con margen. Recomendamos ese enfoque: capturamos el valor real del experimento, sin arriesgar la experiencia de compra de toda nuestra base de golpe. El plan detallado de cómo subir de 1% a 100% de forma segura, con monitoreo y un plan de reversa, está en desarrollo y lo presentamos la próxima semana." Fíjate en que el mensaje nombra los dos resultados con el mismo peso, usa los números exactos de la comparación (no solo la conclusión), y deja claro que la mecánica completa —el "cómo"— todavía viene, sin prometer detalles que este proyecto no cubre.
Resumen y siguiente paso
En este mini-proyecto convertiste el veredicto de la lección 7 en una decisión numérica y defendible: con decideLaunchApproach(), comparaste "ship a 100% de golpe" (12,500 usuarios afectados, excede por 62.5 veces el límite de 200) contra "empezar con canary 1%" (125 afectados, dentro del límite) y elegiste el canary como el enfoque de lanzamiento de recommendations. Con esto cierras el módulo 1: tienes el marco mental completo —por qué ganar no es lanzar, el lanzamiento como riesgo, el radio de impacto, deploy vs. release, las tres propiedades de un lanzamiento cuidadoso, y el criterio para decidir entre enfoques— antes de tocar una sola herramienta técnica.
Hacia dónde sigues. El módulo 2 te da la primera herramienta concreta: feature flags, el mecanismo que hace posible, en código real, la separación entre deploy y release que nombraste en la lección 5 — con un kill switch que apaga recommendations al instante, sin un nuevo deploy. El módulo 3 diseña la rampa completa del canary que elegiste hoy: 1% → 10% → 50% → 100%, con criterios explícitos para avanzar de una etapa a la siguiente. El módulo 4 construye el dashboard que vigila el guardrail de latencia en cada etapa de esa rampa. Y los módulos 5 a 7 completan el resto del ciclo: cuándo revertir, cómo escribir el postmortem sin buscar culpables, y cómo aplicar todo este marco a la capa moderna de enviar modelos de IA. El módulo 8, el capstone de toda la guía, te va a pedir ejecutar las siete piezas juntas sobre este mismo lanzamiento de recommendations — el marco que construiste hoy es, literalmente, el primer paso de esa cadena completa.
Recursos
- Google SRE Workbook, Capítulo 16, "Canarying Releases" — sre.google/workbook/canarying-releases. La referencia formal completa de la práctica que este proyecto ejecutó en versión simplificada: probar un cambio en una porción limitada de producción antes de decidir el resto del rollout. En inglés.
- AWS Well-Architected Framework, Pilar de Confiabilidad, "Implement Change" — docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/implement-change.html. Cierra el módulo donde lo abrió la lección 4: la guía práctica de la industria sobre feature flags y despliegues canary como mecanismos concretos para minimizar el radio de impacto, ahora aplicados a la decisión completa de este proyecto. En inglés.