Feature flags en Java : comment activer des fonctionnalités sans redémarrer l'application
\n\nImaginez : vous avez passé une semaine à développer une nouvelle fonctionnalité — un formulaire de contact avec une suggestion automatique magique. Vous l'avez fusionnée et déployée en production. Et là — un bug. Les utilisateurs ne peuvent pas envoyer de message. Vous devez annuler d'urgence le commit, reconstruire le JAR, redémarrer le serveur. Tout le monde déteste ce moment.
\n\nEt si vous pouviez simplement appuyer sur un bouton "désactiver" directement dans l'interface d'administration, sans toucher au code ? Ou activer la fonctionnalité uniquement pour 10 % des utilisateurs afin de tester la charge ? Ou lancer un test A/B où la moitié voit le nouveau design et l'autre moitié l'ancien ?
\n\nTout cela, c'est la magie des feature flags (aussi appelés feature toggles). Dans cet article, nous allons voir comment les implémenter en Java — du simple Enum aux solutions industrielles comme Togglz et LaunchDarkly. C'est parti !
\n\nQu'est-ce qu'un feature flag et pourquoi en avez-vous besoin ?
\n\nUn feature flag (ou feature toggle) est un interrupteur conditionnel dans le code qui permet d'activer ou de désactiver une fonctionnalité sans modifier le code source ni redéployer l'application.
\n\nCela ressemble à un simple if ? Mais la force réside dans la gestion. Vous stockez l'état des flags dans un fichier de configuration, une base de données ou un service externe. Vous changez la valeur — la fonctionnalité s'active ou se désactive instantanément.
\n\nPourquoi en avez-vous besoin dans les projets Java ?
\n\n- \n
- Trunk-based development — fusionnez des fonctionnalités inachevées dans la branche main en les cachant derrière un flag. Les collègues ne verront pas les bugs, et vous pouvez terminer tranquillement. \n
- Canary releases — activez la fonctionnalité pour 5 % des utilisateurs, surveillez les métriques, puis pour 50 %, puis pour tout le monde. \n
- Kill switch — si une fonctionnalité plante en production, vous la désactivez simplement d'un seul bouton. \n
- Tests A/B — comparez le comportement des utilisateurs avec et sans la fonctionnalité. \n
- Personnalisation — activez les fonctionnalités premium uniquement pour les clients payants. \n
Le risque principal est la dette technique. Si vous ne supprimez pas les anciens flags, le code se transforme en une toile d'araignée de if-else. Nous en parlerons également.
\n\nLa méthode la plus simple : Enum + Properties
\n\nCommençons par une approche de base. Créons une énumération de flags et lisons-les depuis application.properties.
\n\npublic enum FeatureFlag {\n NEW_CHECKOUT_FLOW,\n AI_RECOMMENDATIONS,\n DARK_MODE\n}\n\nChargeons les flags dans un service :
\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\nUtilisation dans le code :
\n\nif (featureFlagService.isEnabled(FeatureFlag.NEW_CHECKOUT_FLOW)) {\n // nouveau flux de commande\n} else {\n // ancien flux\n}\n\nAvantages : simplicité, pas de dépendances inutiles. Inconvénients : pour modifier la valeur, vous devez redémarrer l'application. Ce n'est pas tout à fait "sans redémarrage", mais pour les petits projets, c'est acceptable.
\n\nFlags dynamiques : base de données ou Redis
\n\nSi vous voulez modifier les flags à la volée — stockez-les dans une base de données ou Redis. Créons une table :
\n\nCREATE TABLE feature_flags (\n name VARCHAR(100) PRIMARY KEY,\n enabled BOOLEAN NOT NULL DEFAULT FALSE,\n updated_at TIMESTAMP\n);\n\nService avec mise en cache (pour ne pas solliciter la base de données à chaque requête) :
\n\n@Service\npublic class DynamicFeatureFlagService {\n\n private final JdbcTem