⚙️ Técnico6 min

Feature flags SaaS sin infraestructura: PlanetScale al mando

Feature flags SaaS sin Redis ni externos: PlanetScale como fuente de verdad, un paquete compartido y API mínima por producto para controlar despliegues.

Feature flags SaaS sin infraestructura: PlanetScale al mando
Carlos Martin Pavon

Carlos Martin Pavon

Software Architect & Founder

Los feature flags en SaaS sin infraestructura extra son viables cuando el problema real es controlar despliegues y activar capacidades por vertical, tenant o entorno. La alternativa más simple que encontré: PlanetScale como fuente de verdad, un paquete compartido para tipar y resolver flags, y una API mínima dentro de cada producto. Sin Redis, sin servicios externos, sin panel complejo.

El 85 por ciento de las empresas Fortune 500 usa feature flags para rollouts controlados según datos de 2025. Para un solo founder, LaunchDarkly cuesta desde 300 USD mensuales; una tabla en PlanetScale cuesta 0 USD adicionales con latencia de 1 a 5 milisegundos en la misma región. En mis 6 SaaS compartidos, los feature flags en base de datos evitaron al menos 3 rollbacks de emergencia al permitir desactivar features en segundos.

Por qué no usé un servicio de feature flags dedicado

Tres señales de que necesitás una solución dedicada en lugar de una tabla propia:

  1. Más de 10 ingenieros coordinando flags: la UI y los permisos se vuelven necesarios.
  2. A/B testing estadístico real: el targeting y análisis de conversión requieren infraestructura específica.
  3. Compliance o auditoría formal: algunos sectores requieren trazabilidad certificada de los cambios de flags.

Evaluré LaunchDarkly, Unleash y otros servicios de feature flags. Todos resuelven el problema bien. También agregan infraestructura, una herramienta más que aprender, un servicio más que puede tener downtime, y un costo mensual adicional.

Para mis SaaS de nicho con equipos pequeños y pocos tenants, ese overhead no tiene sentido. El problema que necesitaba resolver era más simple: activar o desactivar capacidades por producto, por tenant y por entorno sin redeployar.

PlanetScale ya estaba en el stack. Los datos de feature flags son pequeños y raramente cambian. La latencia de una query para resolver un flag es aceptable en el contexto de un request web. La solución correcta era la más simple.

La estructura de la solución

SoluciónCosto mensualLatenciaCuándo usarla
Tabla en PlanetScale0 USD adicional1-5 milisegundos1-5 desarrolladores, sin targeting complejo
PostHog Feature Flags0-450 USD~5 milisegundosSi ya usás PostHog para analytics
LaunchDarkly (Growth)300 USDmenos de 1 milisegundoEquipos grandes con A/B testing
Variable de entorno0 USD0 milisegundosSolo para config estática, no por usuario

Tabla de flags en PlanetScale:

CREATE TABLE feature_flags (
  id VARCHAR(36) PRIMARY KEY,
  flag_key VARCHAR(100) NOT NULL,
  tenant_id VARCHAR(36),          -- NULL significa default global
  environment VARCHAR(20) NOT NULL, -- 'production', 'staging', 'development'
  enabled BOOLEAN NOT NULL DEFAULT false,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW() ON UPDATE NOW(),
  INDEX idx_flag_key_tenant_env (flag_key, tenant_id, environment)
);

Paquete compartido @solu30/feature-flags:

El paquete exporta una función resolveFlag(flagKey, tenantId, environment) que hace la query y devuelve un boolean. Los productos la importan y la llaman. No saben nada de la implementación.

La resolución tiene prioridad: flag específico por tenant primero, default global después. Si no hay ningún registro, el flag es false por defecto.

API mínima por producto:

Cada producto tiene un endpoint protegido para que los admins del tenant puedan ver y cambiar sus flags. No es un panel admin complejo: es un formulario simple que llama a la misma tabla.

Cómo funciona la resolución de flags en la práctica

La función resolveFlag hace una query con un ORDER BY que prioriza el registro más específico:

SELECT enabled
FROM feature_flags
WHERE flag_key = :flagKey
  AND environment = :environment
  AND (tenant_id = :tenantId OR tenant_id IS NULL)
ORDER BY tenant_id IS NULL ASC
LIMIT 1;

La lógica es simple: si existe un flag para ese tenant específico, ese gana. Si no, devuelve el default global. Si no hay ningún registro, devuelve false.

Esa query es rápida: el índice compuesto en (flag_key, tenant_id, environment) garantiza que no hay full table scan incluso cuando la tabla crece con cientos de flags.

Caché en memoria para reducir queries

La mayor objeción al enfoque de base de datos es la latencia adicional en cada request que necesita resolver flags. La solución es caché en memoria con TTL corto.

El paquete compartido mantiene un Map en memoria con TTL de 60 segundos por flag. En la mayoría de los casos, la query ni siquiera llega a la base de datos. Cuando el admin cambia un flag, puede invalidar el caché explícitamente mediante un endpoint, o simplemente esperar a que expire el TTL.

Eso es suficiente para la mayoría de los SaaS de nicho: la consistencia de los flags no necesita ser sub-segundo. Un cambio de flag que tarda hasta 60 segundos en propagarse es perfectamente aceptable para activar o desactivar una feature.

Feature flags para control de deploys vs features de tenants

Hay dos usos distintos para los feature flags y el modelo los soporta a ambos.

Control de deploys: un nuevo módulo está en código pero desactivado por defecto en producción. Solo se activa en staging para testear. Cuando la feature está lista, se activa globalmente cambiando el flag default. Ese flujo no requiere redeployar.

Features por tenant: un tenant en el plan premium tiene acceso a un módulo que los tenants del plan básico no tienen. Eso se modela con un flag activado solo para ese tenant_id. La lógica del plan puede crear o actualizar ese registro automáticamente al cambiar el plan del tenant.

La misma tabla, la misma función de resolución, dos casos de uso completamente distintos.

La conexión con el paquete compartido de auth

El mismo principio que aplico a multi-tenant auth Next.js SaaS aplica aquí: la lógica compartida va al paquete, los productos la consumen. El producto no sabe cómo se resuelven los flags; solo llama resolveFlag y usa el resultado.

Esto es coherente con la filosofía de arquitectura SaaS convencional vs innovadora: la solución más simple que resuelve el problema real es la correcta.

Si estás construyendo un SaaS con múltiples verticales y necesitás feature flags compartidos sin agregar infraestructura, trabajo en proyectos de software bajo solu30.

Preguntas frecuentes

¿Cómo implemento feature flags en un SaaS sin infraestructura extra? Usando PlanetScale como fuente de verdad, un paquete compartido para tipar y resolver flags, y una API mínima por producto. Sin Redis, sin servicios externos.

¿Qué ventajas tiene usar PlanetScale para feature flags? Cero infraestructura adicional, los flags viven en la misma base de datos que el resto del sistema, los cambios son auditables, y el equipo no necesita aprender otra herramienta.

¿Cómo resuelvo feature flags por tenant? Con una tabla que tiene la combinación de flag + tenant + entorno. El paquete resuelve con prioridad: tenant específico primero, default global después.

¿Cuándo tiene sentido un servicio dedicado de feature flags? Cuando necesitás A/B testing con análisis estadístico, flags para millones de usuarios con latencia crítica, o targeting por atributos complejos. Para la mayoría de los SaaS de nicho, PlanetScale es suficiente.

#saas#feature-flags#planetscale#arquitectura#multi-saas

Compartir este post

Preguntas frecuentes