main content

Cómo implementar feature flags y despliegue progresivo para lanzar funcionalidades sin riesgo en tu aplicación web a medida

Desplegar código nuevo en producción sigue siendo uno de los momentos donde más se aprietan los dientes en cualquier equipo de desarrollo. Te lo digo por experiencia: un cambio aparentemente inocuo puede provocar una degradación de rendimiento, romper un flujo crítico de negocio o generar una cascada de incidencias en soporte. La pregunta no es si algo va a salir mal. Es cuándo. Y sobre todo, cómo de rápido puedes revertirlo.

Las feature flags y las estrategias de despliegue progresivo existen precisamente para desacoplar el acto de desplegar código del acto de activar funcionalidad. En una aplicación web a medida, donde cada componente se diseña para un contexto de negocio específico, esta separación resulta aún más crítica. No puedes permitirte que un rollback implique perder tres semanas de trabajo de tu equipo. Punto.

Esta guía desglosa la implementación práctica de feature flags, canary releases y rollouts progresivos en el contexto de aplicaciones web desarrolladas a medida, con decisiones de arquitectura, herramientas concretas y patrones de código que puedes aplicar desde el primer sprint.

Qué son las feature flags y por qué lo cambian todo en desarrollo a medida

Una feature flag es un mecanismo de control condicional que permite activar o desactivar una funcionalidad en tiempo de ejecución sin necesidad de redesplegar la aplicación. En su forma más básica, es un condicional que evalúa una configuración externa:

if feature_flags.is_enabled("nuevo_checkout", user=current_user):
    return render_new_checkout(cart)
else:
    return render_legacy_checkout(cart)

Pero reducir las feature flags a un simple if/else es perder de vista su potencial real. Llevo años viendo cómo en una aplicación web a medida las flags se convierten en una capa de gobernanza del producto que permite:

  • Desacoplar deploy de release: el código llega a producción integrado en la rama principal, pero la funcionalidad permanece inactiva hasta que se decide activarla.
  • Validar hipótesis con usuarios reales: puedes exponer una funcionalidad al 5% de tu base de usuarios, medir métricas de conversión y decidir con datos antes de un rollout completo.
  • Gestionar dependencias entre equipos: si el equipo de backend ha terminado su parte pero el frontend necesita dos días más, la flag mantiene todo integrado sin bloquear a nadie.
  • Crear kill switches operativos: ante un pico de carga inesperado, puedes desactivar funcionalidades no críticas (recomendaciones, notificaciones en tiempo real) para proteger el core del sistema.

Traducido: en desarrollo a medida, donde cada cliente tiene requisitos específicos y los ciclos de entrega suelen ser más ajustados que en producto estándar, las feature flags eliminan la fricción entre la velocidad de desarrollo y la estabilidad en producción.

Taxonomía de feature flags: no todas las flags son iguales

Un error que veo constantemente es tratar todas las feature flags como si fueran lo mismo. La realidad es que existen cuatro categorías con ciclos de vida y requisitos técnicos muy diferentes:

Release flags (flags de lanzamiento)

Son las más comunes. Su propósito es ocultar funcionalidad incompleta o pendiente de validación. Tienen un ciclo de vida corto: se crean cuando comienza el desarrollo de una feature y se eliminan cuando el rollout alcanza el 100%. Si una release flag lleva más de 6 semanas activa, probablemente tienes un problema de gestión de producto.

Experiment flags (flags de experimentación)

Controlan variantes de A/B testing o multivariate testing. Requieren integración con sistemas de analítica para medir el impacto de cada variante. Su ciclo de vida es medio: permanecen activas durante la duración del experimento (típicamente 2-4 semanas con tráfico suficiente para alcanzar significancia estadística).

Ops flags (flags operativas)

Son kill switches y circuit breakers controlados por el equipo de operaciones. Permiten degradar funcionalidad de forma graceful ante problemas de rendimiento o dependencias externas caídas. Su ciclo de vida es indefinido: permanecen en el código como mecanismo de resiliencia.

Permission flags (flags de permisos)

Controlan el acceso a funcionalidades premium, features en beta cerrada o capacidades específicas por tenant en aplicaciones multi-tenant. Se integran con el sistema de autorización y su ciclo de vida coincide con el del modelo de negocio.

Cada categoría implica decisiones diferentes de implementación. Las release flags pueden vivir en un fichero de configuración simple; las experiment flags necesitan un sistema de asignación consistente de usuarios a variantes; las ops flags requieren latencia mínima en la evaluación y las permission flags deben integrarse con el modelo de datos del usuario.

Herramientas y plataformas: build vs buy para tu stack

El ecosistema de herramientas para feature flags ha madurado bastante en los últimos años. Te cuento las opciones principales con sus trade-offs para aplicaciones web a medida:

LaunchDarkly

La plataforma más completa del mercado. Ofrece SDKs para prácticamente cualquier lenguaje, evaluación local de flags (sin latencia de red en cada request), segmentación avanzada de usuarios y un sistema robusto de auditoría. Su modelo de pricing basado en Monthly Active Users puede escalar rápido si tu aplicación tiene un volumen alto de usuarios. Es la opción correcta cuando necesitas feature management a escala empresarial con múltiples equipos y entornos.

Unleash

Open source con opción hosted. Proporciona una API REST bien diseñada, SDKs para los lenguajes principales y estrategias de activación configurables (gradual rollout, por IPs, por parámetros custom). Su arquitectura permite self-hosting completo, lo que resulta atractivo cuando tus clientes tienen requisitos estrictos de residencia de datos o compliance. La versión Enterprise añade RBAC, change requests y audit log.

Flagsmith

Otro proyecto open source con opción managed. Destaca por su soporte nativo de remote config (no solo booleans, sino valores de configuración arbitrarios asociados a flags) y su integración con analítica. Buen punto intermedio entre la complejidad de LaunchDarkly y la simplicidad de una solución custom.

Solución custom con evaluación local

Ahora la parte incómoda. Para aplicaciones web a medida con requisitos específicos, a veces la mejor opción es construir un sistema de flags ligero adaptado a tu arquitectura. Un servicio de configuración que expone flags evaluables en el servidor, con un mecanismo de polling o push para actualización en caliente, puede implementarse en menos de una semana de desarrollo si no necesitas segmentación compleja:

// Servicio de flags simplificado con evaluación local
interface FeatureFlag {
  key: string;
  enabled: boolean;
  rolloutPercentage: number;
  allowedSegments: string[];
  metadata: Record<string, unknown>;
}

class FlagService {
  private flags: Map<string, FeatureFlag> = new Map();
  private refreshInterval: NodeJS.Timeout;

  constructor(private configUrl: string, private pollMs = 30_000) {
    this.refresh();
    this.refreshInterval = setInterval(() => this.refresh(), pollMs);
  }

  async refresh(): Promise<void> {
    const response = await fetch(this.configUrl);
    const flags: FeatureFlag[] = await response.json();
    flags.forEach(f => this.flags.set(f.key, f));
  }

  isEnabled(key: string, context?: { userId?: string; segment?: string }): boolean {
    const flag = this.flags.get(key);
    if (!flag || !flag.enabled) return false;

    if (flag.allowedSegments.length > 0 && context?.segment) {
      return flag.allowedSegments.includes(context.segment);
    }

    if (flag.rolloutPercentage < 100 && context?.userId) {
      const hash = this.consistentHash(key, context.userId);
      return hash < flag.rolloutPercentage;
    }

    return flag.enabled;
  }

  private consistentHash(flagKey: string, userId: string): number {
    // Murmur3 o similar para distribución uniforme
    const combined = `${flagKey}:${userId}`;
    let hash = 0;
    for (let i = 0; i < combined.length; i++) {
      hash = ((hash << 5) - hash) + combined.charCodeAt(i);
      hash |= 0;
    }
    return Math.abs(hash) % 100;
  }
}

La decisión build vs buy depende de tres factores: la complejidad de segmentación que necesitas, el número de equipos que van a gestionar flags y si tu cliente acepta dependencias SaaS externas en la cadena crítica de su aplicación. Para la mayoría de aplicaciones web a medida con un equipo de 3-8 desarrolladores, Unleash self-hosted o una solución custom ligera suelen ser el punto óptimo.

Patrones de despliegue progresivo: canary releases y blue-green

Las feature flags controlan qué usuarios ven qué funcionalidad. Los patrones de despliegue progresivo controlan cómo el código nuevo llega a la infraestructura. Son capas complementarias que, combinadas, minimizan el radio de explosión de cualquier problema.

Canary releases

El patrón canary consiste en desplegar la nueva versión a un subconjunto reducido de la infraestructura (típicamente un 1-5% de las instancias) y monitorizar métricas clave antes de continuar el rollout. Si las métricas se degradan, se retira el canary automáticamente.

En la práctica, un pipeline de canary release con Kubernetes se implementa así:

  1. Se despliega un nuevo Deployment con la imagen actualizada, configurado con un número reducido de réplicas.
  2. El Service o Ingress distribuye tráfico entre el Deployment estable y el canary según pesos configurados.
  3. Un controlador (Flagger, Argo Rollouts) monitoriza métricas de Prometheus: latencia p99, tasa de errores 5xx, métricas custom de negocio.
  4. Si las métricas se mantienen dentro de los umbrales definidos durante el período de observación, el controlador incrementa progresivamente el peso del canary.
  5. Si alguna métrica supera el umbral, se ejecuta rollback automático eliminando el Deployment canary.
# Argo Rollouts - Definición de estrategia canary
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: webapp-rollout
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 5
        - pause: { duration: 5m }
        - setWeight: 20
        - pause: { duration: 10m }
        - setWeight: 50
        - pause: { duration: 10m }
        - setWeight: 80
        - pause: { duration: 5m }
      analysis:
        templates:
          - templateName: success-rate
        startingStep: 1
      maxSurge: "20%"
      maxUnavailable: 0

Blue-green deployments

En un despliegue blue-green mantienes dos entornos idénticos en producción. El entorno activo (blue) sirve todo el tráfico. El nuevo código se despliega en el entorno inactivo (green), se ejecutan smoke tests y validaciones, y cuando todo está verificado, se conmuta el tráfico de blue a green mediante un cambio en el balanceador o el DNS.

La ventaja del blue-green sobre el canary es la simplicidad del rollback: basta con reconmutar al entorno anterior, que permanece intacto. La desventaja es el coste de mantener infraestructura duplicada y que el corte es binario (0% o 100%), sin la granularidad de un rollout progresivo.

Para aplicaciones web a medida de tamaño medio, un enfoque híbrido resulta efectivo: blue-green a nivel de infraestructura para garantizar rollback instantáneo, combinado con feature flags a nivel de aplicación para controlar la exposición gradual de funcionalidades individuales.

Progressive delivery con feature flags

¿Lo bonito del modelo? La combinación más potente es usar feature flags como capa de control fino sobre un despliegue progresivo:

  1. El código con la nueva feature se despliega a toda la infraestructura (flag desactivada).
  2. Se activa la flag para el equipo interno (dogfooding).
  3. Se incrementa al 5% de usuarios reales, monitorizando métricas.
  4. Incremento progresivo: 10%, 25%, 50%, 75%, 100%.
  5. Tras una semana estable al 100%, se elimina la flag y el código legacy.

Este patrón te da lo mejor de ambos mundos: despliegue técnico completo (sin ramas de larga duración, sin merge conflicts) con exposición controlada de la funcionalidad.

Gestión de flag debt: el coste oculto que nadie te cuenta

Te lo digo claro: las feature flags son deuda técnica con fecha de caducidad. Cada flag que permanece en el código después de cumplir su propósito añade complejidad cognitiva, incrementa la superficie de testing combinatorio y dificulta la comprensión del flujo real de la aplicación.

El flag debt se acumula de forma silenciosa. Un equipo de 5 desarrolladores creando 3-4 flags por sprint puede acumular 50-60 flags activas en un trimestre. Si no se eliminan las flags que ya están al 100% o las que corresponden a experimentos concluidos, el código se convierte en un laberinto de condicionales anidados.

Estrategias concretas para gestionar el flag debt:

Política de expiración obligatoria: toda flag se crea con una fecha de expiración. Al crearla en el sistema de flags, se establece un expiry_date. Un job periódico alerta cuando una flag ha superado su fecha sin ser eliminada. Herramientas como LaunchDarkly permiten configurar esto nativamente; en soluciones custom, es un cron que consulta la tabla de flags.

Limpieza como parte del Definition of Done: la eliminación del código de una flag (ambas ramas del condicional, dejando solo la rama ganadora) se incluye como tarea del mismo ticket que creó la flag. No se cierra la historia hasta que la flag está limpia.

Métricas de salud de flags: monitoriza el número total de flags activas, la edad media de las flags y el ratio de flags creadas vs eliminadas por sprint. Si el ratio se desequilibra consistentemente, tienes un problema sistémico.

Tooling de detección de flags muertas: scripts que analizan el código buscando flags que están al 100% en producción desde hace más de N días y generan PRs automáticos eliminando el código condicional:

#!/bin/bash
# Detecta flags activas al 100% durante más de 14 días
FLAGS_TO_CLEAN=$(curl -s "$FLAG_SERVICE_URL/api/flags" | \
  jq -r '.[] | select(.rolloutPercentage == 100 and .enabled == true) |
  select((.updatedAt | fromdate) < (now - 14*86400)) | .key')

for flag in $FLAGS_TO_CLEAN; do
  echo "Flag '$flag' lleva >14 días al 100%. Candidata a limpieza."
  # Generar issue o PR automático
done

Separación clara en el código: utiliza un patrón de abstracción que haga explícito dónde están las flags y facilite su eliminación. Evita condicionales de flags dispersos por todo el codebase; centraliza la decisión en un punto y expón el resultado como una dependencia inyectada o un parámetro de configuración del componente.

Observabilidad: cómo saber si tu flag está causando problemas

Desplegar con feature flags sin observabilidad adecuada es como conducir con los ojos vendados y un copiloto que te dice que vayas despacio. Necesitas instrumentación que te permita correlacionar el estado de las flags con el comportamiento del sistema en producción.

Métricas esenciales por flag

Cada flag activa debería exponer como mínimo:

  • Tasa de evaluación: cuántas veces por segundo se evalúa la flag. Una flag que se evalúa 10.000 veces por segundo en un path crítico tiene un impacto de rendimiento diferente a una que se evalúa 10 veces por minuto.
  • Distribución de variantes: qué porcentaje real de evaluaciones resultan en cada variante. Valida que la distribución coincide con la configuración (si configuras 10% y observas 15%, tienes un bug en el hashing).
  • Latencia de evaluación: tiempo que tarda en resolverse la flag. En evaluación local debería ser < 1ms; si estás haciendo llamadas de red por evaluación, tienes un problema de arquitectura.
  • Métricas de negocio segmentadas por variante: conversión, tiempo en página, tasa de error funcional, NPS, lo que sea relevante para la funcionalidad controlada por la flag.

Correlación con alertas

Tu sistema de alertas debe incluir el contexto de flags activas. Cuando salta una alerta de incremento de errores 5xx, la primera pregunta del equipo de guardia es: "¿qué ha cambiado?". Si puedes responder automáticamente "se activó la flag X hace 12 minutos para el segmento Y", reduces el MTTR drásticamente.

Implementa esto exponiendo el estado de flags activas como labels en tus métricas de Prometheus o como atributos en tus traces de OpenTelemetry:

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

def process_order(order, user):
    with tracer.start_as_current_span("process_order") as span:
        # Registrar flags activas en el trace
        active_flags = flag_service.get_active_flags(user)
        for flag_key, variant in active_flags.items():
            span.set_attribute(f"feature_flag.{flag_key}", variant)

        if flag_service.is_enabled("new_payment_flow", user=user):
            span.set_attribute("feature_flag.new_payment_flow.active", True)
            return new_payment_processor.process(order)
        else:
            return legacy_payment_processor.process(order)

Dashboards operativos

Construye un dashboard específico para feature flags que muestre:

  • Listado de flags activas con su estado actual (porcentaje de rollout, segmentos habilitados).
  • Timeline de cambios de estado de flags superpuesto con métricas de sistema (errores, latencia, throughput).
  • Comparativa de métricas clave entre variantes para experiment flags.
  • Alertas de anomalías correlacionadas con cambios recientes en flags.

Grafana con datasource de Prometheus es suficiente para la mayoría de setups. Si usas LaunchDarkly o Unleash, sus integraciones nativas con herramientas de observabilidad simplifican la configuración.

Dónde evaluar las flags en tu aplicación web: la decisión de arquitectura

La ubicación de la evaluación de flags impacta directamente en el rendimiento, la consistencia y la complejidad de tu aplicación. Existen tres puntos de evaluación con trade-offs distintos:

Evaluación en el servidor (SSR / API)

El servidor evalúa la flag y envía al cliente únicamente la variante correspondiente. El usuario nunca ve un flash de contenido ni recibe código de la variante que no le corresponde. Es el enfoque más seguro para funcionalidades sensibles (pricing, features premium) y el que ofrece mejor rendimiento percibido. La contrapartida es que cada cambio de flag requiere que el usuario recargue o que implementes un mecanismo de invalidación.

Evaluación en el cliente (SPA)

El SDK de flags se ejecuta en el navegador y evalúa las flags en tiempo real. Permite cambios instantáneos sin recarga y facilita la experimentación en componentes de UI. El riesgo es el parpadeo de contenido (el usuario ve brevemente la variante por defecto antes de que se resuelva la flag) y que un usuario técnico puede inspeccionar todas las variantes en el bundle de JavaScript.

Evaluación en el edge (CDN / middleware)

Servicios como Cloudflare Workers, Vercel Edge Functions o AWS CloudFront Functions permiten evaluar flags en el edge, cerca del usuario. Es especialmente útil para decisiones de routing (enviar a diferentes backends según la flag) o para cachear variantes de páginas estáticas. La limitación es que el contexto disponible en el edge suele ser más reducido que en el servidor de aplicación.

Para aplicaciones web a medida, la recomendación general es: evalúa en el servidor como regla predeterminada, usa evaluación en cliente solo para flags de UI que no implican lógica de negocio, y reserva el edge para decisiones de routing o A/B testing de páginas completas.

Consistencia de evaluación

Un aspecto crítico y frecuentemente ignorado: un usuario debe ver la misma variante durante toda su sesión (y preferiblemente entre sesiones). Esto se logra mediante hashing determinista del identificador del usuario con la clave de la flag. Si usas hash(userId + flagKey) % 100 < rolloutPercentage, el mismo usuario siempre obtendrá el mismo resultado para el mismo porcentaje de rollout, sin necesidad de almacenar asignaciones en base de datos.

La excepción son las experiment flags con asignación controlada, donde sí necesitas persistir la asignación para garantizar que un usuario no cambia de grupo durante el experimento incluso si el porcentaje se modifica.

De la teoría al primer rollout progresivo

Implementar feature flags y despliegue progresivo no requiere una inversión inicial desproporcionada. Para una aplicación web a medida, el path pragmático es:

  1. Semana 1: implementar un servicio de flags básico (evaluación local, configuración en fichero JSON o tabla de base de datos) con soporte para flags booleanas y rollout por porcentaje.
  2. Semana 2: instrumentar la evaluación de flags con métricas y traces. Integrar el estado de flags en las alertas existentes.
  3. Semana 3: usar la primera flag real en una funcionalidad en desarrollo. Practicar el ciclo completo: despliegue con flag desactivada, activación progresiva, monitorización, rollout al 100%, limpieza de la flag.
  4. Mes 2: evaluar si necesitas migrar a una plataforma más completa (Unleash, Flagsmith) o si la solución custom cubre tus necesidades.

El retorno de esta inversión es inmediato: despliegues que pasan de ser eventos de riesgo coordinados a operaciones rutinarias que cualquier miembro del equipo puede ejecutar con confianza. En una aplicación web a medida, donde cada funcionalidad se construye para un contexto de negocio concreto, la capacidad de lanzar sin riesgo y validar con usuarios reales marca la diferencia entre un proyecto que entrega valor de forma continua y uno que acumula código en ramas esperando el próximo "ventana de despliegue".


Hablemos de tu proyecto web a medida