Déployer du code ne signifie pas toujours publier une fonctionnalité

Votre équipe vient de terminer une grosse refonte de l'algorithme de recherche. Le code est testé, relu et déployé en production. Mais voilà : personne ne l'utilise encore. Le nouvel algorithme est sur le serveur, compilé et prêt, mais chaque utilisateur reçoit toujours les résultats de l'ancien. C'est voulu.

Ce scénario peut sembler étrange si vous avez l'habitude de déploiements où chaque changement impacte immédiatement les utilisateurs. Pourtant, c'est un modèle puissant qui sépare deux choses que la plupart des équipes traitent comme une seule : déployer du code et publier une fonctionnalité.

Le problème que le déploiement seul ne résout pas

Les déploiements canary et les rollouts progressifs résolvent bien un problème : ils contrôlent la quantité de trafic qui atteint une nouvelle version de votre application. Vous envoyez 5 % des utilisateurs vers la nouvelle version, vous surveillez les erreurs, puis vous montez à 100 %. Cela fonctionne quand vous voulez déployer une version entière progressivement.

Mais que faire si vous voulez activer une fonctionnalité spécifique uniquement pour certains utilisateurs, sans déployer différentes versions du code ? Peut-être voulez-vous que les testeurs internes voient les nouveaux résultats de recherche pendant que tout le monde reste sur l'ancien algorithme. Peut-être voulez-vous activer une fonctionnalité d'abord pour les utilisateurs d'une région. Peut-être voulez-vous tester un nouveau flux de paiement avec 10 % des utilisateurs, mais uniquement s'ils sont sur l'application mobile.

Les déploiements canary et les rollouts progressifs ne résolvent pas cela. Ils opèrent au niveau de la version, pas au niveau de la fonctionnalité. Vous avez besoin de quelque chose qui vit dans votre code et prend des décisions par requête, par utilisateur, par session.

Feature flags : une couche de contrôle dans votre code

Les feature flags sont des vérifications conditionnelles dans votre code qui déterminent si une fonctionnalité doit être active pour un utilisateur donné. L'idée clé est que la condition ne repose pas sur la version du code en cours d'exécution. Elle repose sur une configuration qui peut changer à tout moment, sans redéploiement.

Voici à quoi cela ressemble en pratique. Votre équipe termine le nouvel algorithme de recherche. Le code est déployé en production dans le cadre de la dernière version. Mais à l'intérieur de la fonction de recherche, il y a une vérification :

Voici un exemple JavaScript de ce même modèle :

const featureFlags = require('./feature-flags');

function search(query, user) {
  if (featureFlags.isEnabled('new-search', user)) {
    return newSearchAlgorithm(query);
  } else {
    return oldSearchAlgorithm(query);
  }
}

// Le flag est désactivé par défaut, donc tous les utilisateurs passent par l'ancien chemin.
// Quand vous êtes prêt, activez le flag pour les testeurs via le tableau de bord.
if feature_flag.is_active("new_search_algorithm", user):
    return new_search_algorithm(query)
else:
    return old_search_algorithm(query)

Le flag est désactivé par défaut. Chaque utilisateur reçoit toujours l'ancien algorithme, même si le nouveau code est sur le même serveur. Quand vous êtes prêt, vous activez le flag pour les testeurs internes. Ils commencent à voir les nouveaux résultats. Vous surveillez leur comportement. Si quelque chose cloche, vous désactivez le flag. Pas de rollback. Pas de redéploiement. Juste un changement de configuration qui prend effet en quelques secondes.

Pourquoi séparer le déploiement de la publication ?

Le premier avantage est à la fois psychologique et pratique : déployer cesse d'être un événement à haut risque. Quand chaque déploiement active tous les changements immédiatement, vous accumulez le travail, testez intensément et retenez votre souffle pendant les fenêtres de mise en production. Quand déploiement et publication sont séparés, vous pouvez déployer quotidiennement, voire plusieurs fois par jour, sachant que le nouveau code reste inactif jusqu'à ce que vous l'activiez explicitement.

Le deuxième avantage est le contrôle granulaire. Les feature flags ne sont pas simplement activés ou désactivés pour tout le monde. Vous pouvez définir des règles de ciblage :

  • Actif pour les utilisateurs avec des IDs spécifiques
  • Actif pour les utilisateurs dans certaines régions
  • Actif pour les testeurs internes ou les utilisateurs bêta
  • Actif pour un pourcentage du total des utilisateurs

Ces règles peuvent se cumuler. Vous pouvez commencer avec 5 % des utilisateurs, observer pendant une journée, passer à 25 %, puis à 50 %, puis à 100 %. Chaque changement se fait via un tableau de bord ou un appel API. Pas de modification de code. Pas de déploiement.

Le troisième avantage est l'interrupteur d'arrêt d'urgence. Quand une fonctionnalité pose problème après avoir été activée, vous pouvez la désactiver depuis un seul endroit. C'est beaucoup plus rapide que d'attendre la fin d'un pipeline de rollback. En quelques secondes, la fonctionnalité problématique est désactivée, et les utilisateurs reviennent à l'ancien comportement. Pour les incidents de production, ces secondes comptent.

Le coût caché : la dette de flags

Les feature flags ajoutent de la complexité à votre codebase. Chaque flag introduit une branche conditionnelle qui doit être maintenue. Si votre équipe crée des flags et ne les supprime jamais, votre code se remplit de conditions mortes dont personne ne se souvient.

Un scénario courant : un feature flag est créé pour un nouveau flux de paiement. Le flux est déployé avec succès pour 100 % des utilisateurs. L'ancien code de paiement est toujours là, protégé par un flag toujours désactivé. Des mois plus tard, un nouveau développeur voit le flag et se demande s'il est toujours pertinent. Personne ne sait. Le flag reste parce que le supprimer semble risqué.

C'est la dette de flags, et c'est le principal coût opérationnel des feature flags. Les flags ont besoin d'un cycle de vie : créer, activer, surveiller, nettoyer. Quand une fonctionnalité est entièrement déployée pour tous les utilisateurs, l'ancien chemin de code et le flag doivent être supprimés. Le flag a rempli son rôle. Le garder ne fait qu'ajouter de la charge cognitive et augmenter la surface d'attaque pour les bugs.

Choisir votre approche de gestion des flags

Pour les petites équipes ou les cas d'usage simples, les feature flags peuvent commencer comme des variables d'environnement ou des fichiers de configuration que l'application lit au démarrage. Cela fonctionne, mais avec une limitation : modifier un flag nécessite un redémarrage ou un rechargement de la configuration.

Pour les équipes plus grandes ou les règles de ciblage plus complexes, des plateformes dédiées comme LaunchDarkly, Split ou Flagr offrent une évaluation en temps réel des flags, un ciblage utilisateur et des journaux d'audit. Ces plateformes vous permettent de modifier les flags depuis un tableau de bord et de voir l'effet immédiatement sur toutes les instances en cours d'exécution.

Le bon choix dépend de votre échelle. Commencez simplement. Ajoutez de la complexité seulement quand vous en avez besoin.

Checklist pratique pour utiliser les feature flags

  • Chaque flag a un propriétaire clair et un objectif documenté dans un endroit visible
  • Les flags ont une date de suppression prévue ou une condition de déclenchement (ex. : "supprimer quand le déploiement atteint 100 %")
  • Les anciens chemins de code sont supprimés quand le flag n'est plus nécessaire
  • Les modifications de flags sont journalisées avec qui a changé quoi et quand
  • Les flags critiques ont un tableau de bord de surveillance montrant qui voit quelle variante
  • L'équipe a une cadence régulière de nettoyage des flags obsolètes

Ce qu'il faut retenir

Les feature flags vous offrent une couche de contrôle qui fonctionne indépendamment de votre pipeline de déploiement. Les déploiements canary et les rollouts progressifs contrôlent la quantité de trafic qui atteint une nouvelle version. Les feature flags contrôlent quels utilisateurs voient quelles fonctionnalités au sein de la même version. Utilisez-les ensemble, et vous obtenez un contrôle fin sur la façon dont les changements atteignent vos utilisateurs. N'oubliez pas que chaque flag que vous créez est une dette technique qui doit être nettoyée une fois son travail terminé.