Feature flags con PostgreSQL: despliegues graduales sin romper producción

Un feature flag permite activar o desactivar una capacidad sin volver a desplegar todo el servicio. Bien usado, separa el momento de publicar código del momento de exponerlo a usuarios. Mal usado, crea una colección de condicionales imposibles de auditar. PostgreSQL puede ser una base sencilla y fiable para comenzar, siempre que el modelo incluya alcance, auditoría y fecha de retiro.

Índice de Contenido 💻
  1. Qué debe resolver un sistema pequeño de flags
  2. Un esquema PostgreSQL que conserva el contexto
  3. Haz el rollout determinista
  4. Lee la configuración de forma coherente
  5. Separa el apagado de emergencia del despliegue
  6. Evita que los flags se conviertan en deuda
  7. Pruebas mínimas antes de aumentar el porcentaje
  8. Fuentes y lectura técnica

Qué debe resolver un sistema pequeño de flags

Antes de construir un panel, define tres preguntas: quién puede ver la función, en qué porcentaje se habilita y cómo se apaga rápidamente. No mezcles en una misma bandera un experimento, un permiso permanente y un interruptor operativo. Tienen vidas útiles y reglas diferentes.

  • Release flag: oculta una función incompleta durante un despliegue y debe retirarse pronto.
  • Experiment flag: asigna cohortes estables para comparar variantes.
  • Operational flag: desactiva una parte costosa si el sistema está bajo presión.
  • Permission flag: habilita una capacidad para un plan, organización o grupo.

Un esquema PostgreSQL que conserva el contexto

CREATE TABLE feature_flags (
  key text PRIMARY KEY,
  enabled boolean NOT NULL DEFAULT false,
  rollout_percent smallint NOT NULL DEFAULT 0
    CHECK (rollout_percent BETWEEN 0 AND 100),
  environment text NOT NULL,
  expires_at timestamptz,
  owner text NOT NULL,
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE feature_flag_audit (
  id bigserial PRIMARY KEY,
  flag_key text NOT NULL REFERENCES feature_flags(key),
  actor text NOT NULL,
  old_value jsonb,
  new_value jsonb NOT NULL,
  reason text,
  created_at timestamptz NOT NULL DEFAULT now()
);

expires_at y owner convierten una promesa informal en una tarea mantenible. La auditoría permite saber qué cambió, quién lo hizo y por qué. En producción conviene añadir una restricción única para cada combinación de bandera y entorno si separas esos campos en tablas distintas.

Haz el rollout determinista

Un porcentaje no debe cambiar de opinión en cada petición. Calcula un hash estable usando la clave de la bandera y el identificador del usuario u organización. Convierte ese hash en un número entre 0 y 99 y activa la función si cae dentro del porcentaje. Así, un usuario permanece en la misma cohorte mientras el porcentaje no cambie.

function isInRollout(flagKey, subjectId, percent) {
  const digest = createHash('sha256')
    .update(`${flagKey}:${subjectId}`)
    .digest();
  const bucket = digest.readUInt32BE(0) % 100;
  return bucket < percent;
}

Elige una sola unidad de asignación. Si la función afecta a una cuenta empresarial, usa el identificador de la organización y no el del usuario; de lo contrario dos compañeros podrían ver comportamientos incompatibles.

Lee la configuración de forma coherente

Una lectura de flags debe tener un comportamiento claro durante una actualización concurrente. PostgreSQL usa Read Committed por defecto: cada sentencia ve los datos confirmados antes de comenzar. Para cambiar una bandera y escribir su auditoría, usa una sola transacción, de modo que nunca aparezca el valor nuevo sin su registro de cambio.

BEGIN;
SELECT enabled, rollout_percent
  FROM feature_flags
  WHERE key = 'new-checkout' AND environment = 'production'
  FOR UPDATE;

UPDATE feature_flags
   SET enabled = true, rollout_percent = 10,
       updated_at = now()
 WHERE key = 'new-checkout' AND environment = 'production';

INSERT INTO feature_flag_audit
  (flag_key, actor, new_value, reason)
VALUES
  ('new-checkout', 'amauris', '{"enabled":true,"percent":10}',
   'canary inicial');
COMMIT;

El bloqueo de fila evita que dos cambios administrativos se pisen sin control. La transacción no debe incluir llamadas lentas a servicios externos. Guarda el cambio y deja que la aplicación lea el resultado mediante caché con una caducidad corta.

Separa el apagado de emergencia del despliegue

Un kill switch debe ser pequeño, visible y seguro por defecto. Define qué ocurre si PostgreSQL está temporalmente inaccesible: una función crítica puede permanecer apagada, mientras una función no esencial puede usar el último valor conocido. Documenta esa decisión; no dejes que cada módulo invente su propio fallback.

Si usas caché, invalida la clave después de un cambio o acepta una ventana de propagación conocida. La consistencia perfecta no es necesaria en todos los casos, pero sí debes saber cuánto tiempo puede tardar en llegar el apagado a cada instancia.

Evita que los flags se conviertan en deuda

Cada bandera debe nacer con un propietario, una razón, una fecha de revisión y una condición de retiro. Cuando la nueva ruta sea estable, elimina el flag y el código antiguo. Las banderas permanentes de permisos u operaciones pueden quedarse, pero deben tener nombres claros y pruebas específicas.

Una práctica útil es fallar en CI cuando una bandera supera su fecha de vencimiento. Otra es incluir una página de inventario que muestre flags activos, entornos, porcentaje, propietario y última modificación. El objetivo no es crear burocracia, sino hacer visible el coste de cada interruptor.

Pruebas mínimas antes de aumentar el porcentaje

  • La bandera apagada conserva el comportamiento anterior.
  • El porcentaje 0 no habilita usuarios y el 100 no deja usuarios fuera.
  • El mismo sujeto recibe la misma variante en peticiones repetidas.
  • La actualización de configuración y su auditoría se confirman juntas.
  • Una lectura fallida aplica el fallback documentado.
  • El apagado funciona con caché caliente y con varias instancias.
  • La bandera tiene propietario y fecha de retiro.

Fuentes y lectura técnica

La explicación de Martin Fowler sobre feature toggles distingue flags de release, experimentación, operaciones y permisos, y advierte sobre el coste de mantenerlos. La documentación de transacciones de PostgreSQL explica la atomicidad de los cambios, mientras que la documentación de aislamiento describe la concurrencia de Read Committed.

Si quieres conocer otros artículos parecidos a Feature flags con PostgreSQL: despliegues graduales sin romper producción puedes visitar la categoría Blog.

También puede gustarte:

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir