Módulo 2: Feature Flags

Presentación del módulo: el feature flag, el interruptor que separa deploy de release

Por qué este módulo, ahora

El módulo 1 terminó con una decisión concreta, no con una intención vaga. El equipo de Mercado comparó, con números, dos formas de lanzar recommendations —el carrusel de recomendaciones que ganó el experimento (+18.75% en conversión, p=0.0114) pero rompió el guardrail de latencia (p95 910ms, techo 800ms)—: "ship a 100% de golpe" exponía a 12,500 compradores a esa regresión ya conocida; "empezar con canary 1%" exponía a solo 125, dentro del límite de 200 que el propio equipo se fijó. El proyecto del módulo 1 eligió el canary al 1%. Y ahí se detuvo, a propósito: decidir cuánta gente exponer primero es una cosa; tener, en código real, la forma de exponer exactamente a esa gente y a nadie más es otra completamente distinta.

Esa es la brecha que abre este módulo. Todo el marco mental del módulo 1 —el radio de impacto, las tres propiedades de un lanzamiento cuidadoso, la separación conceptual entre deploy y release que nombró la lección 5— asumía que existía alguna forma de controlar qué porcentaje de usuarios ve un cambio, sin decir todavía cuál. Este módulo contesta esa pregunta con la primera herramienta técnica real de la guía: el feature flag. No es una idea abstracta más — es una función que vas a ejecutar hoy mismo, isEnabled(userId, flag), que decide, para cada comprador de Mercado, si ve recommendations o no, sin que nadie tenga que volver a deployar una sola línea de código para cambiar esa respuesta.

Conexión con el módulo 1. La lección 5 de ese módulo, "Deployar no es lo mismo que lanzar", dejó nombrada la distinción —código en producción (deploy) contra usuarios viéndolo (release)— y dijo, explícitamente, que el mecanismo que la hace posible en código real quedaba pendiente para este módulo. Este es ese módulo. Todo lo que sigue da por aprendido el vocabulario de deploy vs. release; lo que construye encima es el cómo.

Una analogía: el cableado ya está, tú decides cuándo prender la luz

Cuando se construye una casa, el electricista instala el cableado completo, conecta cada foco a su circuito, prueba que la corriente llega — y nada de eso enciende una sola luz por sí solo. Encender la luz de la sala es un acto separado, deliberado, que cualquier persona con acceso al interruptor puede hacer en el momento que decida: hoy, mañana, o nunca, si la sala todavía no está lista para recibir visitas. El cableado terminado es el deploy: el trabajo de ingeniería, completo, esperando. El interruptor prendido es el release: la decisión de negocio de que, a partir de ahora, la luz de verdad se ve.

El feature flag es, literalmente, ese interruptor. No es el cableado —el código de recommendations ya está deployado en los servidores de Mercado, terminado, probado— es el mecanismo que decide, en cada instante y para cada usuario, si la luz está prendida o apagada. Y como cualquier interruptor de pared, tiene una propiedad que vas a usar todo este módulo: cambiar su posición no requiere volver a cablear la casa. Prender o apagar la luz de la sala no exige llamar al electricista de nuevo — exige, únicamente, mover la palanca. Cambiar el estado de un feature flag no exige un nuevo deploy — exige, únicamente, cambiar un valor de configuración.

El mapa de las ocho lecciones de este módulo

Lección   Pregunta que contesta
────────  ──────────────────────────────────────────────────────────────
L1        (esta) ¿Dónde nos dejó el módulo 1, y qué herramienta falta?
L2        ¿Qué hace, en código, que un flag separe deploy de release?
L3        ¿Cómo es, por dentro, un feature flag real? ¿Dónde vive?
L4        ¿Cómo expones el flag a solo un porcentaje de usuarios?
L5        ¿Cómo apagas todo al instante si algo sale mal?
L6        ¿Todos los flags son iguales, o cumplen roles distintos?
L7        ¿Qué pasa con un flag cuando ya cumplió su propósito?
L8        Proyecto: pon recommendations detrás de un flag real

La lección 2 responde la pregunta más urgente primero: ¿qué distingue, en código, a un feature flag de un simple if escrito adentro de una función? La respuesta —que el interruptor vive afuera del código, no adentro— es la que hace posible todo lo que sigue. La lección 3 formaliza esa idea en una estructura concreta: los campos que tiene un flag real (name, enabled, rolloutPercent, y más) y dónde se guarda esa estructura en un sistema real. La lección 4 es el corazón técnico del módulo: cómo un solo flag puede exponer una feature a exactamente el 10% de los usuarios, de forma estable, sin Math.random() — ahí construyes isEnabled(userId, flag) con un hash determinista, la función que vas a reusar en el resto de la guía. La lección 5 le agrega al mismo flag el mecanismo de emergencia: el kill switch, que apaga a todos al instante sin importar el porcentaje. La lección 6 da un paso atrás y clasifica: no todos los flags tienen el mismo propósito ni la misma vida útil, y confundir un flag de experimento con uno operacional tiene consecuencias reales. La lección 7 cierra el arco temático con la cara incómoda de la herramienta: los flags que sobreviven a su propósito se acumulan, y esa acumulación tiene un nombre —deuda de flags— y un costo medible. La lección 8, el proyecto, junta las cuatro piezas técnicas (flag, porcentaje, determinismo, kill switch) sobre el caso real de recommendations.

La frontera: qué NO entra en este módulo

Este módulo construye el mecanismo del flag. No construye todavía las herramientas que se apoyan en él, aunque las vas a nombrar de pasada varias veces:

  • Cómo se diseña la rampa completa —1% → 10% → 50% → 100%, con criterios explícitos para avanzar de una etapa a la siguiente— es el módulo 3. Aquí vas a ver isEnabled(userId, flag) funcionando a un porcentaje fijo (10%); ahí vas a ver cómo y cuándo ese porcentaje sube.
  • Cómo se vigila el guardrail de latencia mientras el porcentaje sube es el módulo 4. Este módulo te da el interruptor; ese módulo te da el dashboard que te dice si conviene seguir subiendo.
  • Qué hacer cuando el kill switch ya se activó —el runbook, la severidad del incidente, si se revierte o se arregla hacia adelante— es el módulo 5. Aquí vas a ejecutar el kill switch como mecanismo puro (enabled: false apaga a todos); el proceso completo de responder a un incidente real queda para ese módulo.
  • La infraestructura que deploya el código a los servidores —pipeline de CI/CD, build de producción— sigue siendo, como ya estableció el módulo 1, territorio de fullstack-performance-and-deployment-guide. Un feature flag no reemplaza ese pipeline: asume que el código ya está deployado, y decide, aparte, quién lo ve.

Errores comunes

Pensar que este módulo enseña a hacer el rollout gradual completo. Qué pasa: alguien termina la lección 4 —donde isEnabled expone al 10%— y espera que la siguiente lección le muestre cómo subir automáticamente a 50% y luego a 100%. Por qué pasa: "exponer al 10%" y "subir de 10% a 100%" se sienten como el mismo problema, solo que en una escala mayor. Cómo detectarlo: la pregunta que aparece es "¿y cómo hago para que suba solo cuando esté listo?" — esa es, literalmente, la pregunta que contesta el módulo 3, no este. Cómo corregirlo: este módulo fija un porcentaje y lo respeta (rolloutPercent: 10, quieto); decidir cuándo y cuánto subir ese número es un problema distinto, con su propia mecánica, que empieza en la siguiente puerta.

Confundir un if cualquiera con un feature flag. Qué pasa: alguien escribe una condicional dentro del código —if (isNewUser) showRecommendations()— y la llama "un feature flag", sin que exista ningún mecanismo externo para cambiar esa condición sin volver a deployar. Por qué pasa: cualquier bifurcación en el código parece un interruptor, porque técnicamente decide entre dos comportamientos. Cómo detectarlo: la pregunta correctora es "¿puedo cambiar esto sin tocar el código?" — si la respuesta es no, es una condicional común, no un flag. Cómo corregirlo: la lección 2 de este módulo construye exactamente esa distinción con código ejecutable, comparando las dos versiones lado a lado.

Creer que tener un feature flag ya hace que un lanzamiento sea seguro. Qué pasa: el equipo pone recommendations detrás de un flag y considera el trabajo de seguridad terminado, sin haber pensado todavía en qué porcentaje usar, si hay un kill switch de verdad probado, o qué tipo de flag es este y cuándo se va a quitar. Por qué pasa: el flag es la pieza más visible y la que primero se construye, y es fácil confundir "tener la herramienta" con "haberla usado bien". Cómo detectarlo: si nadie puede contestar "¿a qué porcentaje está el flag ahora mismo, y por qué ese número?", el flag existe pero la decisión de uso todavía no se tomó. Cómo corregirlo: las lecciones 4 a 7 son, precisamente, las decisiones que convierten un flag —una pieza técnica neutral— en una práctica de lanzamiento segura: qué porcentaje, con qué estabilidad, con qué plan de apagado, y con qué fecha de retiro.

Ejercicios

Ejercicio 1 — Ubica la pregunta. Para cada pregunta, di si la contesta el módulo 1 (ya visto), este módulo 2, o un módulo posterior (3, 4 o 5, sin necesidad de acertar el número exacto):

  • (a) "¿Cuántos compradores de Mercado quedarían expuestos si lanzamos recommendations al 100% de golpe?"
  • (b) "¿Cómo hago, en código, para que solo el 10% de los compradores vea recommendations, y que sea siempre el mismo 10%?"
  • (c) "Si el guardrail de latencia se rompe a la mitad del rollout, ¿subimos a 50% de todos modos o nos quedamos en 10%?"
Ver solución
  • (a) Módulo 1 — blastRadius() de la lección 4 contestó exactamente esta pregunta, con el número 12,500.
  • (b) Este módulo 2 — es exactamente lo que isEnabled(userId, flag) construye en la lección 4, con hash determinista y rolloutPercent.
  • (c) Un módulo posterior (el 4, monitoreo, o el 5, rollback) — decidir si subir o quedarse depende de vigilar el guardrail en vivo, que este módulo no cubre; aquí el porcentaje se fija, no se decide en tiempo real.

Ejercicio 2 — El interruptor y el cableado. Usando la analogía de esta lección, explica en dos frases por qué "deployar recommendations" y "activar el feature flag de recommendations" pueden ser, perfectamente, dos eventos separados por semanas.

Ver solución

Deployar es como terminar el cableado: el código de recommendations queda instalado, corriendo, probado en los servidores de Mercado — pero el interruptor (el flag) puede seguir apagado indefinidamente, sin que nadie lo vea. Activar el flag es como prender la luz: una decisión aparte, tomada cuando el equipo decide que es el momento correcto (por ejemplo, después de fijar el porcentaje de exposición y de probar el kill switch), que no requiere volver a tocar el cableado que ya estaba terminado.

Ejercicio 3 — Predicción. Sin haber leído todavía la lección 4, ¿qué problema crees que aparece si, en vez de un hash determinista del userId, la asignación de quién ve recommendations se decidiera con Math.random() en cada request? Piensa en qué le pasaría a un mismo comprador que visita la página de producto dos veces en el mismo día.

Ver solución

Con Math.random(), cada llamada a la función de asignación tira un dado nuevo, sin memoria de las llamadas anteriores — así que el mismo comprador podría ver recommendations en su primera visita del día y no verla en la segunda, sin ningún cambio real en el porcentaje de rollout ni en el flag. Esa inconsistencia rompe la experiencia (¿por qué el carrusel desapareció?) y, peor todavía, rompe cualquier intento de medir el efecto de la feature con rigor —la guía de métricas necesita que un usuario esté consistentemente en control o en variant, no saltando entre los dos—. Por eso la lección 4 exige, específicamente, un hash determinista: la misma entrada (userId) siempre produce la misma salida.

Resumen y siguiente paso

En esta lección ubicaste este módulo en el arco completo de la guía: el módulo 1 decidió cuánto exponer (canary al 1%, 125 usuarios afectados dentro del límite de 200); este módulo 2 construye el mecanismo que hace posible exponer exactamente a esa gente y a nadie más — el feature flag, el interruptor que separa deploy (el cableado) de release (la luz prendida). Viste el mapa de las ocho lecciones que siguen y la frontera con los módulos 3, 4 y 5, que dan por hecho que el flag ya existe.

Antes de avanzar deberías poder: explicar con tus propias palabras la analogía del cableado y el interruptor; distinguir la pregunta que contesta este módulo (el mecanismo del flag) de las que contestan los módulos siguientes (la rampa, el monitoreo, el incidente); y anticipar por qué un hash determinista, y no Math.random(), es la pieza que hace posible una experiencia estable para cada usuario.

La lección 2 arranca con la pregunta más concreta posible: ¿qué tiene, en código, un feature flag real que no tiene un simple if escrito adentro de una función? Vas a ver las dos versiones lado a lado, ejecutadas, y la diferencia va a quedar imposible de confundir.

Recursos

  • Pete Hodgson (con Martin Fowler), "Feature Toggles (aka Feature Flags)" — martinfowler.com/articles/feature-toggles.html. El artículo de referencia sobre feature flags como mecanismo de software; desarrollado en detalle a lo largo de todo este módulo. En inglés.
  • Charity Majors, "Deploys Are the WRONG Way to Change User Experience" — honeycomb.io/blog/deploys-wrong-way-change-user-experience. El argumento completo sobre por qué deploy y release deben ser eventos separados, la premisa sobre la que se construye este módulo entero. En inglés.
  • LaunchDarkly, "What Is Progressive Delivery All About?" — launchdarkly.com/blog/what-is-progressive-delivery-all-about. Una introducción práctica a cómo los feature flags habilitan la exposición gradual y los kill switches, el tema completo de las lecciones 4 y 5. En inglés.