Módulo 2: Feature Flags

Tipos de flag: temporal, de experimento, operacional, permanente

Descripción

Las lecciones anteriores usaron recommendations como el flag de ejemplo, y por diseño se pareció siempre al mismo tipo de flag: uno que empieza apagado, sube de porcentaje con el tiempo, y algún día llega a 100%. Pero Mercado tiene, en su registro, flags que no siguen ese patrón en absoluto — el kill switch de la lección 5 no "sube de porcentaje" nunca, y un flag de experimento A/B no debería llegar nunca a 100% mientras el experimento siga corriendo. Esta lección clasifica los flags en cuatro tipos, con propósitos y ciclos de vida distintos, porque tratarlos a todos igual —como si todos fueran a desaparecer solos en unas semanas— es exactamente el error que abre la puerta a la lección 7.

Conexión con el módulo. Esta lección usa el campo type que ya estaba en el registro desde la lección 3 (type: 'release' para recommendations, type: 'experiment' para checkoutVariantB), y le da, por fin, un propósito completo. La lección 7 va a usar esta misma clasificación para decidir, con criterio, cuáles flags son candidatos a quitarse y cuáles no.

Una analogía: cuatro tipos de cinta adhesiva, para cuatro trabajos distintos

Una caja de herramientas bien surtida no tiene un solo tipo de cinta adhesiva para todo — tiene cinta de pintor, pensada para quitarse en un día sin dejar residuo; cinta de embalaje, para sellar una caja que se va a abrir en semanas; cinta aislante, que se queda instalada indefinidamente protegiendo un cable; y cinta de señalización permanente, pintada en el piso de una bodega para marcar un carril, pensada para durar años. Usar cinta de pintor para marcar un carril de bodega se despega en una semana y deja el carril sin marcar; usar cinta de señalización permanente para pintar una pared que se va a repintar el mes siguiente deja un desastre imposible de quitar.

Los cuatro tipos de feature flag son, literalmente, esa misma lógica aplicada a software: cada uno está diseñado para durar un tiempo distinto, y usar el tipo equivocado para el trabajo —tratar un flag que debería vivir para siempre como si fuera temporal, o dejar vivo por meses un flag pensado para durar una semana— produce el mismo tipo de desastre que la cinta mal elegida.

Ejemplo trabajado: los cuatro tipos, con los flags reales de Mercado

Vamos a clasificar cuatro flags del registro de Mercado, con la guía de qué significa cada tipo:

function describeFlagType(type) {
  const guide = {
    release: 'temporal -- vive mientras el rollout esta en progreso; se quita cuando llega a 100%',
    experiment: 'temporal -- vive mientras el A/B esta corriendo; se quita cuando el experimento cierra',
    operational: 'permanente -- un kill switch operativo, se queda para el proximo incidente',
    permanent: 'permanente -- una decision de negocio de largo plazo (config regional, plan de precios)',
  };
  return guide[type];
}

const mercadoFlags = [
  { name: 'recommendations', type: 'release', enabled: true, rolloutPercent: 10 },
  { name: 'checkoutVariantB', type: 'experiment', enabled: true, rolloutPercent: 50 },
  { name: 'disableSellerPayouts', type: 'operational', enabled: false, rolloutPercent: 100 },
  { name: 'euPriceDisplay', type: 'permanent', enabled: true, rolloutPercent: 100 },
];

console.log('=== Tipos de flag activos en Mercado ===\n');
mercadoFlags.forEach((f) => {
  console.log(f.name.padEnd(22) + 'type=' + f.type.padEnd(13) + '-> ' + describeFlagType(f.type));
});

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

=== Tipos de flag activos en Mercado ===

recommendations       type=release      -> temporal -- vive mientras el rollout esta en progreso; se quita cuando llega a 100%
checkoutVariantB      type=experiment   -> temporal -- vive mientras el A/B esta corriendo; se quita cuando el experimento cierra
disableSellerPayouts  type=operational  -> permanente -- un kill switch operativo, se queda para el proximo incidente
euPriceDisplay        type=permanent    -> permanente -- una decision de negocio de largo plazo (config regional, plan de precios)

Fíjate en la columna que de verdad importa: no type, sino la palabra que abre cada descripción —temporal o permanente—. Los cuatro flags usan exactamente la misma mecánica técnica (isEnabled(), el mismo hashUserId(), el mismo chequeo de enabled primero); lo único que los distingue es cuánto tiempo se espera que vivan en el código, y esa expectativa es la que determina si un flag viejo es normal o es un problema.

Los cuatro tipos, uno por uno

release — el tipo que usaste en las lecciones 2 a 5 con recommendations. Envuelve una feature nueva mientras sube de 0% a 100%; una vez que llega a 100% y se confirma que se queda ahí, el flag ya cumplió su propósito completo y el código debería, eventualmente, dejar de necesitar la condición if (isEnabled(...)) — la feature simplemente pasa a ser parte normal del producto. Vida esperada: semanas, no meses.

experiment — el tipo de checkoutVariantB. Envuelve una variante que se está comparando contra un control, generalmente al 50% para que la muestra de cada grupo sea comparable (el mismo tipo de experimento que la guía de métricas de este ecosistema analiza con rigor estadístico). Vive exactamente mientras el experimento corre; cuando el z-test da un resultado —gane o pierda variant—, el flag de experimento se retira, y si ganó, se reemplaza por un flag de tipo release que hace el rollout completo del ganador. Vida esperada: días a semanas, atada a cuánto tarde el experimento en juntar significancia.

operational — el tipo del kill switch, disableSellerPayouts. No sigue ningún rollout, no tiene un "100% final" hacia el que avanzar — existe para apagar algo de emergencia (aquí, los pagos a vendedores, si un sistema de fraude detecta algo raro) y se espera que se quede en el código de forma indefinida, listo para el próximo incidente. Fíjate en su estado: enabled: false no significa "abandonado" — significa "el interruptor de emergencia está en su posición normal de reposo, listo para activarse si hace falta". Vida esperada: permanente, mientras la funcionalidad que protege siga existiendo.

permanent — el tipo de euPriceDisplay, que decide si se muestra el precio con impuestos incluidos (regulación europea) o sin ellos. No es un experimento ni un rollout — es una decisión de negocio de largo plazo que varía según el contexto (aquí, la región del comprador), y se espera que exista mientras exista la regla de negocio que representa. A diferencia del operational, normalmente está activo todo el tiempo para el segmento que le corresponde, no esperando un incidente. Vida esperada: años, o hasta que cambie la regla de negocio misma.

Errores comunes

Tratar un flag experiment como si fuera release, y dejarlo subir a 100% sin cerrar el experimento. Qué pasa: el equipo, entusiasmado porque checkoutVariantB parece ir bien en las primeras métricas, sube rolloutPercent más allá del 50% que el diseño del experimento necesitaba, antes de que la guía de métricas haya calculado la significancia. Por qué pasa: subir el porcentaje se siente como progreso, y la disciplina de "un experimento necesita grupos comparables hasta el final" es fácil de perder de vista con buenos resultados preliminares. Cómo detectarlo: si rolloutPercent de un flag experiment cambió antes de que exista un resultado estadístico cerrado, el experimento ya está comprometido —la muestra dejó de ser comparable—. Cómo corregirlo: un flag experiment mantiene su rolloutPercent fijo (normalmente 50/50) hasta que el experimento cierra formalmente; solo entonces se reemplaza por un flag release, que sí tiene sentido subir hacia 100%.

Confundir operational con "flag olvidado que nadie usa". Qué pasa: alguien revisando el registro de flags ve disableSellerPayouts con enabled: false desde hace meses, sin ningún cambio, y lo marca como candidato a eliminar, asumiendo que un flag que "no hace nada" hace tiempo es deuda. Por qué pasa: la intuición de "si no cambió en meses, está abandonado" es correcta para un flag release, pero exactamente incorrecta para uno operational — su valor está precisamente en quedarse quieto, listo, sin necesitar cambios frecuentes. Cómo detectarlo: antes de marcar cualquier flag como candidato a eliminar, revisa su type — si es operational, la pregunta correcta no es "¿cuánto hace que no cambió?" sino "¿la funcionalidad que protege todavía existe?" Cómo corregirlo: la lección 7 construye exactamente este criterio: los flags operational y permanent no cuentan como deuda por su antigüedad, a diferencia de los release y experiment.

Crear un flag sin decidir su tipo desde el principio. Qué pasa: alguien agrega un flag nuevo al registro sin pensar todavía si es un rollout temporal, un experimento, un interruptor operacional o una decisión permanente — y meses después, nadie en el equipo puede decir con confianza si ese flag debería haberse quitado ya o si está cumpliendo su función correctamente. Por qué pasa: en el momento de crear el flag, la urgencia suele ser "que funcione ya", y clasificar su ciclo de vida se siente como un paso administrativo que puede esperar. Cómo detectarlo: si el registro de Mercado tiene flags sin un type claro, o con un type que nadie puede justificar, la clasificación se saltó. Cómo corregirlo: como en el ejemplo de esta lección, decidir el type es parte de crear el flag, no un paso posterior — determina, desde el día uno, qué expectativa de vida tiene y quién debería revisarlo con qué frecuencia.

Ejercicios

Ejercicio 1 — Clasifica un flag nuevo. El equipo de precios de Mercado quiere probar, con el 20% de los compradores, un algoritmo de descuentos dinámicos, comparando contra el algoritmo actual, durante dos semanas, antes de decidir si lo adopta. ¿Qué type le corresponde, y qué debería pasar con el flag al final de esas dos semanas?

Ver solución

type: 'experiment' — está comparando dos algoritmos con una fracción fija de usuarios, durante un periodo definido, antes de tomar una decisión basada en el resultado. Al final de las dos semanas, si el algoritmo nuevo gana, el flag de experimento se retira y se reemplaza por un flag release que hace el rollout gradual completo del ganador hacia el 100% (siguiendo el mismo patrón que recommendations); si pierde, el flag de experimento simplemente se retira, y el algoritmo actual se queda como estaba, sin necesidad de ningún flag nuevo.

Ejercicio 2 — Encuentra la clasificación incorrecta. Un compañero clasifica disableSellerPayouts (el kill switch de pagos a vendedores) como type: 'release', porque "algún día ya no lo vamos a necesitar". ¿Qué está mal con ese razonamiento?

Ver solución

Confunde "no se está usando activamente ahora mismo" (enabled: false, en reposo) con "eventualmente termina y se retira" (la definición de release). Un flag release avanza hacia un 100% definitivo y luego desaparece del código porque la feature que envolvía ya es parte normal del producto. disableSellerPayouts no avanza hacia ningún estado final — su valor completo está en quedarse disponible indefinidamente, listo para el próximo incidente de fraude o error en pagos. Clasificarlo como release lo pondría, incorrectamente, en la lista de candidatos a eliminar de la lección 7, exactamente el error que describe la sección de errores comunes de esta lección.

Ejercicio 3 — Los cuatro tipos, con tus propias palabras. Sin mirar la tabla de esta lección, escribe de memoria una frase de una línea para cada uno de los cuatro tipos (release, experiment, operational, permanent), explicando cuánto tiempo debería vivir cada uno.

Ver solución

No hay una única redacción correcta, pero cada frase debería capturar la idea central: release — vive mientras un rollout está en progreso, y desaparece cuando llega a 100% y se confirma estable. experiment — vive mientras un A/B test junta datos, y se retira en cuanto el experimento cierra con un resultado. operational — vive indefinidamente, como un mecanismo de emergencia listo para el próximo incidente. permanent — vive indefinidamente, representando una regla de negocio de largo plazo que no depende de ningún experimento ni rollout. La distinción que separa a los cuatro en dos grupos —temporales (release, experiment) contra permanentes (operational, permanent)— es exactamente lo que la lección 7 usa para decidir qué es deuda y qué no.

Resumen y siguiente paso

En esta lección clasificaste los flags de Mercado en cuatro tipos, según su propósito y su vida esperada: release (temporal, hacia un 100% final), experiment (temporal, atado a la duración de un A/B test), operational (permanente, un kill switch listo para el próximo incidente) y permanent (permanente, una regla de negocio de largo plazo). Viste que la misma mecánica técnica —el mismo isEnabled()— sirve para los cuatro, y que lo único que los distingue es cuánto tiempo se espera que vivan en el código.

Antes de avanzar deberías poder: clasificar un flag nuevo en uno de los cuatro tipos, dado su propósito; explicar por qué operational y permanent no son "flags olvidados" aunque no cambien en meses; y anticipar que solo dos de los cuatro tipos —release y experiment— tienen una fecha de retiro natural.

La lección 7 cierra el arco temático del módulo con la consecuencia directa de esta clasificación: qué pasa cuando un flag release o experiment sobrevive más allá de su fecha de retiro natural, y cómo se detecta —y se cobra— esa deuda antes de que se acumule sin control.

Recursos

  • Pete Hodgson (con Martin Fowler), "Feature Toggles (aka Feature Flags)" — martinfowler.com/articles/feature-toggles.html. La sección "Categories of Toggle" define exactamente los cuatro tipos de esta lección (Release, Experiment, Ops y Permissioning Toggles) y su vida esperada, la fuente directa de esta clasificación. En inglés.
  • LaunchDarkly, "What Is a Kill Switch in Software Development?" — launchdarkly.com/blog/what-is-a-kill-switch-software-development. Describe los kill switches como mecanismos de seguridad permanentes, distintos de los flags de rollout temporal — la base del tipo operational de esta lección. En inglés.