Feature flags en Java: cómo activar funciones sin reiniciar la aplicación

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Feature flags en Java: cómo activar funciones sin reiniciar la aplicación

\n\n

Imagine: pasó una semana desarrollando una nueva función — un formulario de contacto con autocompletado mágico. Lo fusionó, lo desplegó a producción. Y entonces — un error. Los usuarios no pueden enviar mensajes. Tiene que revertir el commit urgentemente, recompilar el JAR, reiniciar el servidor. Todos odian ese momento.

\n\n

¿Y si pudiera simplemente presionar un botón de "desactivar" directamente en el panel de administración, sin tocar el código? ¿O activar la función solo para el 10% de los usuarios para probar la carga? ¿O ejecutar una prueba A/B donde la mitad ve el nuevo diseño y la mitad el antiguo?

\n\n

Todo esto es la magia de los feature flags (también conocidos como feature toggles). En este artículo analizaremos cómo implementarlos en Java — desde un Enum primitivo hasta soluciones industriales como Togglz y LaunchDarkly. ¡Allá vamos!

\n\n

¿Qué es un feature flag y para qué sirve?

\n\n

Un feature flag (o feature toggle) es un interruptor condicional en el código que permite activar o desactivar funcionalidades sin modificar el código fuente ni redesplegar la aplicación.

\n\n

¿Suena como un simple if? Pero el poder está en la gestión. Usted almacena el estado de los flags en un archivo de configuración, base de datos o servicio externo. Cambia el valor — la función se activa o desactiva al instante.

\n\n

¿Para qué sirve esto en proyectos Java?

\n\n
    \n
  • Trunk-based development — fusione funciones incompletas en main, ocultándolas detrás de un flag. Los colegas no verán errores y usted las termina tranquilamente.
  • \n
  • Canary releases — active la función para el 5% de los usuarios, observe las métricas, luego para el 50%, luego para todos.
  • \n
  • Kill switch — si la función falla en producción, simplemente la desactiva con un botón.
  • \n
  • Pruebas A/B — compare el comportamiento de los usuarios con y sin la función.
  • \n
  • Personalización — active funciones premium solo para clientes que pagan.
  • \n
\n\n

El principal riesgo es la deuda técnica. Si no elimina los flags antiguos, el código se convierte en una telaraña de if-else. También hablaremos de esto.

\n\n

La forma más simple: Enum + Properties

\n\n

Empecemos con el enfoque básico. Creamos una enumeración de flags y los leemos desde application.properties.

\n\n
public enum FeatureFlag {\n    NEW_CHECKOUT_FLOW,\n    AI_RECOMMENDATIONS,\n    DARK_MODE\n}
\n\n

Cargamos los flags en un servicio:

\n\n
@Component\npublic class FeatureFlagService {\n\n    private final Map<FeatureFlag, Boolean> flags = new HashMap<>();\n\n    public FeatureFlagService(@Value("${features.new-checkout-flow:false}") boolean newCheckout,\n                              @Value("${features.ai-recommendations:false}") boolean aiRec,\n                              @Value("${features.dark-mode:false}") boolean darkMode) {\n        flags.put(FeatureFlag.NEW_CHECKOUT_FLOW, newCheckout);\n        flags.put(FeatureFlag.AI_RECOMMENDATIONS, aiRec);\n        flags.put(FeatureFlag.DARK_MODE, darkMode);\n    }\n\n    public boolean isEnabled(FeatureFlag flag) {\n        return flags.getOrDefault(flag, false);\n    }\n}
\n\n

Uso en el código:

\n\n
if (featureFlagService.isEnabled(FeatureFlag.NEW_CHECKOUT_FLOW)) {\n    // nuevo flujo de pago\n} else {\n    // flujo antiguo\n}
\n\n

Ventajas: simplicidad, sin dependencias adicionales. Desventajas: para cambiar el valor, necesita reiniciar la aplicación. No es exactamente "sin reiniciar", pero para proyectos pequeños está bien.

\n\n

Flags dinámicos: base de datos o Redis

\n\n

Si desea cambiar los flags sobre la marcha — guárdelos en una base de datos o Redis. Creamos una tabla:

\n\n
CREATE TABLE feature_flags (\n    name VARCHAR(100) PRIMARY KEY,\n    enabled BOOLEAN NOT NULL DEFAULT FALSE,\n    updated_at TIMESTAMP\n);
\n\n

Servicio con caché (para no golpear la base de datos en cada solicitud):

\n\n
@Service\npublic class DynamicFeatureFlagService {\n\n    private final JdbcTem                

Blogs

Book Recommendations