Módulo 4: North Star And Guardrails
Guardrails: lo que no debe empeorar mientras optimizas
Descripción
Un guardrail es una métrica que el equipo se compromete a no dejar empeorar más allá de un umbral definido, mientras optimiza la North Star o cualquiera de sus inputs. No es una métrica que quieras subir a propósito —para eso ya tienes la North Star y sus inputs (lecciones 3 y 4)—; es una métrica de vigilancia, que solo importa cuando cruza una línea que el equipo dibujó antes de empezar a optimizar nada, no después de que algo salió mal.
La lección anterior mostró el riesgo: perseguir checkoutConversionRate sin cuidado puede subir el número objetivo mientras el reembolso y el fraude se disparan por debajo, sin que nadie lo note hasta el reporte trimestral. Un guardrail existe exactamente para acortar esa distancia — convertir "nos vamos a enterar en tres meses" en "nos enteramos esta misma semana, con una alerta automática".
Conexión con el módulo. Esta lección construye la herramienta que responde a la advertencia de la lección 5: guardrailCheck(), una función que compara el estado de un conjunto de métricas antes y después de un cambio contra sus umbrales, y marca cuáles se rompieron. Es la pieza final antes de la síntesis de la lección 7, y la que el mini-proyecto de la lección 8 reutiliza, sin cambios, sobre la decisión final de lanzar recommendations.
Una analogía: el cinturón de seguridad mientras aceleras
Cuando manejas en una autopista, el velocímetro es tu North Star del viaje: quieres llegar rápido, así que lo miras y aceleras. Pero nadie maneja mirando solo el velocímetro. El cinturón de seguridad está puesto, sin que lo pienses, desde antes de arrancar — no porque esperes chocar, sino porque si algo sale mal, necesitas que ya esté ahí, no instalarlo después del impacto. El límite de velocidad de la zona escolar tampoco se negocia a mitad de camino porque "hoy vas con prisa" — está fijo, definido antes de manejar, y si lo cruzas, algo tiene que ceder: bajas la velocidad, sin importar cuánto quieras llegar rápido.
Los guardrails de producto funcionan igual en las dos formas. Se definen antes de empezar a optimizar, no se improvisan cuando algo ya se rompió (el cinturón puesto de antemano). Y cuando se cruzan, no son una sugerencia — son una señal de que hay que frenar la optimización, aunque el número principal se vea espectacular (el límite de velocidad que no se negocia). checkoutLatencyP95Ms, complaintRate, churnRate y grossMargin son, para el lanzamiento de recommendations, exactamente ese cinturón y ese límite.
Ejemplo trabajado: guardrailCheck() sobre el lanzamiento de recommendations
Definimos cada guardrail con tres cosas: la métrica, el tipo de umbral (ceiling — un techo que no se puede cruzar; floor — un piso que no se puede perforar; maxIncrease — cuánto puede subir, como máximo, respecto al estado anterior), y el límite exacto.
// guardrailCheck: compara el estado de un conjunto de metricas guardrail antes/despues
// de un cambio contra sus umbrales, y marca cuales se ROMPIERON. Modelo pedagogico:
// no reemplaza el A/B test con significancia (eso es M5-M7); aqui solo se declara
// la regla de decision -- que umbral no se puede cruzar -- antes de lanzar.
function guardrailCheck(before, after, guardrails) {
const results = guardrails.map((g) => {
const beforeVal = before[g.metric];
const afterVal = after[g.metric];
let broken = false;
let detail = '';
if (g.type === 'ceiling') {
broken = afterVal > g.limit;
detail = afterVal + ' vs techo ' + g.limit;
} else if (g.type === 'floor') {
broken = afterVal < g.limit;
detail = afterVal + ' vs piso ' + g.limit;
} else if (g.type === 'maxIncrease') {
const delta = afterVal - beforeVal;
broken = delta > g.limit;
detail = 'delta +' + delta.toFixed(4) + ' vs maximo permitido +' + g.limit;
}
return { metric: g.metric, before: beforeVal, after: afterVal, broken, detail };
});
const brokenGuardrails = results.filter((r) => r.broken).map((r) => r.metric);
return { results, anyBroken: brokenGuardrails.length > 0, brokenGuardrails };
}
// El lanzamiento de recommendations: el carrusel sube la conversion de checkout
// (una input metric del North Star), pero agrega una llamada extra al motor de
// recomendaciones antes de renderizar la pagina de producto.
const before = {
checkoutConversionRate: 0.032,
checkoutLatencyP95Ms: 650,
complaintRate: 0.012,
churnRate: 0.045,
grossMargin: 0.220,
};
const after = {
checkoutConversionRate: 0.038,
checkoutLatencyP95Ms: 910,
complaintRate: 0.014,
churnRate: 0.046,
grossMargin: 0.219,
};
const guardrails = [
{ metric: 'checkoutLatencyP95Ms', type: 'ceiling', limit: 800 },
{ metric: 'complaintRate', type: 'maxIncrease', limit: 0.005 },
{ metric: 'churnRate', type: 'maxIncrease', limit: 0.010 },
{ metric: 'grossMargin', type: 'floor', limit: 0.180 },
];
console.log('=== guardrailCheck: lanzamiento de recommendations en Mercado ===\n');
const lift = ((after.checkoutConversionRate - before.checkoutConversionRate) / before.checkoutConversionRate) * 100;
console.log(' checkoutConversionRate (input metric): ' + (before.checkoutConversionRate * 100).toFixed(1) + '% -> ' +
(after.checkoutConversionRate * 100).toFixed(1) + '% (+' + lift.toFixed(2) + '% relativo)\n');
const check = guardrailCheck(before, after, guardrails);
check.results.forEach((r) => {
console.log(' ' + r.metric.padEnd(22) + String(r.before).padStart(8) + ' -> ' + String(r.after).padStart(8) +
' ' + (r.broken ? 'ROTO' : 'OK ') + ' (' + r.detail + ')');
});
console.log('\n' + (check.anyBroken
? 'RED FLAG: ' + check.brokenGuardrails.length + ' guardrail(s) roto(s): ' + check.brokenGuardrails.join(', ')
: 'Todos los guardrails se mantienen dentro del umbral.'));
Qué esperar. Al correr el archivo con Node, la salida es exactamente esta:
=== guardrailCheck: lanzamiento de recommendations en Mercado ===
checkoutConversionRate (input metric): 3.2% -> 3.8% (+18.75% relativo)
checkoutLatencyP95Ms 650 -> 910 ROTO (910 vs techo 800)
complaintRate 0.012 -> 0.014 OK (delta +0.0020 vs maximo permitido +0.005)
churnRate 0.045 -> 0.046 OK (delta +0.0010 vs maximo permitido +0.01)
grossMargin 0.22 -> 0.219 OK (0.219 vs piso 0.18)
RED FLAG: 1 guardrail(s) roto(s): checkoutLatencyP95Ms
Lee el resultado con cuidado, porque es exactamente el tipo de situación para la que existen los guardrails: la noticia principal es buena — checkoutConversionRate, la input metric que recommendations debía mover, subió un 18.75% relativo—. Si el equipo solo mirara ese número, la conclusión sería "lanzamos a todos, ya". Pero checkoutLatencyP95Ms —el tiempo que tarda en cargar la página para el 95% de los usuarios más lentos— pasó de 650ms a 910ms, cruzando el techo de 800ms definido de antemano. La causa es plausible y concreta: el carrusel de recomendaciones agrega una llamada al motor de recomendaciones antes de terminar de renderizar la página de producto, y esa llamada extra está retrasando a una parte de los usuarios. complaintRate, churnRate y grossMargin se mantienen dentro de rango — no todo se rompió—, pero un solo guardrail roto es suficiente para que guardrailCheck() marque RED FLAG.
Qué hacer con un RED FLAG — y por qué no es lo mismo que "cancelar"
Un guardrail roto no significa, automáticamente, "recommendations fracasó, revertir todo". Significa "detente antes de lanzar al 100%, y resuelve esto primero". En el caso de hoy, la lectura correcta no es abandonar el carrusel de recomendaciones —la ganancia en conversión es real y sustancial—, es diagnosticar la causa técnica del guardrail roto: ¿la llamada al motor de recomendaciones puede hacerse en paralelo con el resto del renderizado, en vez de bloquearlo? ¿Puede cachearse para usuarios recurrentes? ¿Hace falta un timeout que oculte el carrusel si tarda demasiado, en vez de retrasar toda la página?
Esta distinción —entre "revertir la apuesta" y "arreglar el problema técnico antes de escalarla"— es la razón por la que los guardrails se revisan antes de decidir enviar al 100% de los usuarios, y por qué esta guía todavía no ha llegado al módulo donde se corre el A/B test con significancia estadística (módulos 5 al 7). Ahí es donde se va a decidir, con rigor, si el lift en checkoutConversionRate es real o azar — pero esa decisión, sin importar cuán positiva sea, siempre se lee junto con el estado de los guardrails, nunca sola.
Cuántos guardrails, y de qué tipo
Cuatro guardrails, como en el ejemplo de hoy, es un número razonable — ni tan pocos que dejen puntos ciegos obvios, ni tantos que generen ruido (revisar veinte umbrales cada semana termina en que nadie los revisa en serio, el mismo error de "demasiadas métricas" que ya viste en el módulo 1). Para elegir cuáles, pregúntate: ¿qué se rompería si optimizáramos la North Star de la forma más agresiva y menos cuidadosa posible? Esa pregunta suele señalar directamente a los candidatos correctos:
- Rendimiento técnico (
checkoutLatencyP95Ms): un cambio que hace más lenta la experiencia, aunque suba la conversión hoy, suele costar retención mañana. - Satisfacción / quejas (
complaintRate): la señal más temprana de que algo se está sintiendo mal para el usuario, mucho antes de que se refleje en cualquier número financiero. - Retención / churn (
churnRate): protege contra ganar una compra hoy a costa de perder al comprador para siempre. - Márgenes (
grossMargin): protege contra crecer el volumen de ventas de una forma que, en el fondo, no es rentable.
Nota que esta lista no es la misma que la de Ronny Kohavi cuando acuñó el término "guardrail metric" para experimentación: sus guardrails originales son, sobre todo, de confianza en el experimento mismo —por ejemplo, el sample ratio mismatch, que detecta si la aleatorización entre control y variant salió mal—. Los guardrails de este módulo son de negocio: protegen al producto y a la empresa, no la validez estadística del experimento en sí. Vas a ver los guardrails de confianza de Kohavi cuando la guía llegue al A/B test formal, en el módulo 6 — las dos familias de guardrails conviven, y un experimento serio necesita ambas.
Errores comunes
Guardrails que nadie mira hasta que explota el churn. Qué pasa: el equipo define correctamente sus cuatro guardrails al planear el lanzamiento, pero después nadie los vuelve a revisar hasta que, semanas más tarde, alguien nota en una reunión mensual que churnRate viene subiendo desde el lanzamiento. Por qué pasa: definir guardrails se siente como el trabajo completo, y falta la segunda mitad —revisarlos con la misma frecuencia y disciplina que la North Star—; una métrica de vigilancia que nadie vigila no protege nada. Cómo detectarlo: si preguntas "¿cuándo fue la última vez que alguien corrió guardrailCheck() sobre el lanzamiento de recommendations?" y la respuesta es "no lo sé" o "antes de lanzar, nada más", los guardrails están en el papel, no en la práctica. Cómo corregirlo: los guardrails se revisan con la misma cadencia que el funnel del módulo 2 —semanal, como mínimo—, no una sola vez al definirlos. Como recordó la lección 1, un guardrail que se calculó una vez y se archivó no está protegiendo nada.
Definir los umbrales después de ver los datos, no antes. Qué pasa: alguien corre checkoutLatencyP95Ms después del lanzamiento, ve que subió a 910ms, y entonces decide que "800ms hubiera sido un buen límite" — el umbral se ajusta a lo que ya pasó, en vez de haberse fijado antes de que pasara nada. Por qué pasa: es más fácil justificar un número una vez que ya conoces el resultado que comprometerte a uno de antemano, cuando todavía hay incertidumbre real sobre qué va a pasar. Cómo detectarlo: si el umbral de un guardrail se documentó por primera vez después de ver los datos del lanzamiento, no protegió nada — solo describió, en retrospectiva, lo que ya había ocurrido. Cómo corregirlo: los cuatro umbrales del ejemplo de hoy —800ms, +0.5pp, +1pp, 18%— se definen en la reunión de planeación del experimento, antes del primer usuario expuesto a recommendations, exactamente como se hizo en este ejemplo.
Tratar cualquier guardrail roto como si todos pesaran lo mismo. Qué pasa: el equipo ve RED FLAG en el reporte de guardrailCheck() y reacciona igual sin importar cuál guardrail se rompió — un guardrail de latencia (arreglable con trabajo de ingeniería en días) recibe la misma urgencia que uno de churn (una señal mucho más seria de que el producto está dañando la relación con el usuario a largo plazo). Por qué pasa: guardrailCheck() devuelve la misma palabra —ROTO— sin distinguir severidad, y es fácil tratar la salida de una función como si fuera la decisión final, en vez de un insumo para el juicio del equipo. Cómo corregirlo: la salida de guardrailCheck() te dice qué se rompió, no qué tan grave es — eso requiere la misma discusión en equipo de siempre. Un guardrail técnico (latencia) suele resolverse sin tocar la apuesta de producto; un guardrail de negocio (churn, márgenes) suele exigir repensar el diseño del feature mismo, no solo optimizarlo.
Ejercicios
Ejercicio 1 — Corre guardrailCheck() a mano con un umbral distinto. Si el equipo hubiera definido el techo de checkoutLatencyP95Ms en 950ms en vez de 800ms, ¿el guardrail seguiría rompiéndose con los mismos datos (before: 650, after: 910)? Verifica tu respuesta corriendo la función con el umbral cambiado.
Ver solución
No se rompería. Con limit: 950, la condición afterVal > g.limit evalúa 910 > 950, que es false — el guardrail pasaría como OK. Este ejercicio es, a propósito, incómodo: muestra que el resultado de guardrailCheck() depende por completo de dónde se fijó el umbral, y que un umbral elegido sin cuidado (demasiado laxo) puede dejar pasar un problema real como si no hubiera pasado nada. Es la misma responsabilidad que ya viste con evaluateNorthStar() en la lección 3: la herramienta es tan buena como los números que alguien, con criterio, decidió poner en ella.
Ejercicio 2 — Diseña un guardrail nuevo para Mercado. El equipo de soporte de Mercado propone agregar un quinto guardrail: avgSupportResponseTimeHours (tiempo promedio de respuesta a un ticket de soporte), con un techo de 6 horas. Si before fuera 4.2 horas y after (tras el lanzamiento de recommendations) fuera 7.1 horas, ¿qué tipo de umbral (ceiling, floor, o maxIncrease) usarías, y el guardrail se rompería?
Ver solución
Sería un guardrail de tipo ceiling — como checkoutLatencyP95Ms, es una métrica donde importa que no cruce un techo absoluto, no cuánto cambió respecto al before. Con { metric: 'avgSupportResponseTimeHours', type: 'ceiling', limit: 6 }, la condición evalúa 7.1 > 6, que es true — el guardrail se rompería. Esto sugiere que el aumento en volumen de tickets (probablemente relacionado con la misma causa que subió complaintRate, aunque ese guardrail específico no se haya roto) está sobrecargando al equipo de soporte lo suficiente como para que el tiempo de respuesta se salga de rango — una señal más de que el problema de latencia del carrusel merece atención antes de escalar el lanzamiento.
Ejercicio 3 — Distingue guardrail de negocio de guardrail de confianza. De estos cuatro candidatos a guardrail, ¿cuáles son "de negocio" (protegen al producto/empresa, el tipo de esta lección) y cuáles son "de confianza en el experimento" (protegen la validez estadística, el tipo original de Kohavi)? (a) sampleRatioMismatch — si la proporción real de usuarios en control vs variant se desvía de la proporción esperada (por ejemplo, 48/52 en vez de 50/50). (b) grossMargin. (c) crossContamination — si usuarios de control accidentalmente ven código de variant, o viceversa.
Ver solución
(a) De confianza. Un sampleRatioMismatch no dice nada sobre si recommendations es bueno o malo para el negocio — dice que algo salió mal en la aleatorización misma, y que los resultados del experimento, sean cuales sean, no se pueden confiar hasta arreglarlo. (b) De negocio — exactamente el tipo de esta lección, protege la rentabilidad del negocio mientras se optimiza la North Star. (c) De confianza — si hay contaminación cruzada entre grupos, la comparación control vs variant deja de medir lo que se supone que mide, sin importar qué tan bien o mal le vaya al producto. Los dos guardrails "de confianza" de este ejercicio van a reaparecer, formalizados, cuando la guía llegue al A/B test del módulo 6.
Resumen y siguiente paso
En esta lección construiste y ejecutaste guardrailCheck(), que compara el estado de un conjunto de métricas antes y después de un cambio contra sus umbrales, y marca cuáles se rompieron. Sobre el lanzamiento de recommendations, el resultado fue mixto pero informativo: checkoutConversionRate subió un 18.75% relativo —una buena noticia—, pero checkoutLatencyP95Ms cruzó su techo de 800ms —un RED FLAG que exige diagnóstico técnico antes de escalar, no necesariamente cancelar la apuesta—. Viste también la distinción entre guardrails de negocio (los de esta lección) y guardrails de confianza en el experimento (los de Kohavi, que reaparecen en el módulo 6).
Antes de avanzar deberías poder: explicar la diferencia entre un guardrail de tipo ceiling, floor, y maxIncrease; correr guardrailCheck() a mano sobre un umbral distinto; y describir qué hacer —y qué NO hacer— cuando un guardrail se rompe.
La lección 7 junta las tres piezas del módulo —North Star, input metrics, guardrails— en un solo panorama: el sistema completo que gobierna las decisiones del equipo de Mercado, no cada pieza por separado.
Recursos
- Ronny Kohavi, "Guardrail Metrics for A/B Tests" — linkedin.com/pulse/guardrail-metrics-ab-tests-ronny-kohavi. El artículo del propio Kohavi, coautor de Trustworthy Online Controlled Experiments, sobre guardrails de confianza en el experimento —distintos de los guardrails de negocio de esta lección, pero de la misma familia de ideas—. En inglés.
- Mixpanel, "Guardrail metrics: The complete guide to balanced product growth" — mixpanel.com/blog/guardrail-metrics. Casos reales de Instagram, Airbnb y Spotify usando guardrails para detectar cuándo una mejora en una métrica dañaba a otra parte del producto. En inglés.
- Statsig, "What are guardrail metrics in A/B tests?" — statsig.com/blog/what-are-guardrail-metrics-in-ab-tests. Explica la selección de guardrails con ejemplos de Airbnb, Netflix y Uber, y advierte contra elegir demasiados (ruido) o demasiado pocos (puntos ciegos) — el mismo balance que discute esta lección. En inglés.