Retour au blog
juin 2026·4 min de lectureGénéré par IA
Langue:DeutschEnglishFrançaisÖsterreichischKlingonian Version originale: English

Feature toggles avec une configuration par environnement

#backend#devops#architecture

La façon la plus simple de livrer une fonctionnalité inachevée en production sans l'exposer est aussi celle qui est le plus souvent réinventée de travers : un booléen lu depuis la configuration, vérifié dans un if. Avant de recourir à un service de feature flags avec tableau de bord et SDK, il vaut la peine de savoir jusqu'où un simple toggle basé sur l'environnement peut vous mener, et c'est plus loin que la plupart des équipes ne le pensent.

L'idée de base

Un feature toggle est une vérification à l'exécution qui décide si un chemin de code s'exécute. La valeur du flag vient de l'extérieur du code (variables d'environnement, fichier de configuration, ligne de base de données) : on peut donc changer le comportement sans nouveau déploiement.

const features = {
  newCheckoutFlow: process.env.FEATURE_NEW_CHECKOUT === 'true',
  betaExportApi: process.env.FEATURE_EXPORT_API === 'true',
};

function handleCheckout(req, res) {
  if (features.newCheckoutFlow) {
    return handleCheckoutV2(req, res);
  }
  return handleCheckoutV1(req, res);
}

C'est tout le mécanisme. Tout le reste (déploiements par pourcentage, ciblage par utilisateur, interrupteurs d'urgence) n'est que du raffinement par-dessus « lire une valeur et bifurquer dessus ».

Pourquoi s'en soucier, plutôt que de simplement déployer la fonctionnalité

  • Découpler le déploiement de la mise en production. Fusionnez et déployez le code derrière un flag à false, puis activez-le séparément, sans déploiement, quand vous êtes vraiment prêt. La mise en production devient un changement de configuration plutôt qu'un changement de code, plus rapide à faire et plus rapide à annuler.
  • Interrupteurs d'urgence. Si un nouveau chemin de code se comporte mal en production, désactiver un flag est immédiat. Annuler un déploiement prend plus de temps et revient sur tout ce que ce déploiement contenait, pas seulement sur la fonctionnalité défaillante.
  • Comportement propre à l'environnement. Activez une fonctionnalité en préproduction des semaines avant la production, sans maintenir une branche séparée.

Une couche de configuration qui passe à l'échelle au-delà de quelques booléens

Éparpiller des tests process.env.FEATURE_X === 'true' dans toute la base de code marche pour deux flags et devient ingérable à dix. Centralisez :

// config/features.js
function bool(name, fallback = false) {
  const value = process.env[name];
  if (value === undefined) return fallback;
  return value === 'true' || value === '1';
}

module.exports = {
  newCheckoutFlow: bool('FEATURE_NEW_CHECKOUT'),
  betaExportApi: bool('FEATURE_EXPORT_API'),
  strictRateLimit: bool('FEATURE_STRICT_RATE_LIMIT', true), // defaults on
};

Désormais, chaque site d'appel importe depuis un seul endroit, chaque flag a une ligne qui documente sa valeur par défaut, et ajouter un flag est un changement d'une ligne.

const features = require('./config/features');

if (features.betaExportApi) {
  app.use('/api/export', exportRouter);
}

Gérer les flags qui exigent un ciblage par utilisateur

Les variables d'environnement sont globales : chaque requête de ce processus voit la même valeur. C'est suffisant pour « cette fonctionnalité est-elle active en production, tout court », mais pas pour « montrer ceci à 5 % des utilisateurs » ou « montrer ceci aux comptes internes seulement ». Dans ce cas, la vérification du flag a besoin d'un élément du contexte de la requête :

function isEnabledForUser(flagName, user) {
  if (!features[flagName]) return false;

  // internal accounts always see flags that are otherwise off for everyone
  if (user?.isInternal) return true;

  // deterministic percentage rollout: same user always gets the same bucket
  const bucket = hashToPercent(`${flagName}:${user.id}`);
  return bucket < rolloutPercent(flagName);
}

function hashToPercent(input) {
  let hash = 0;
  for (let i = 0; i < input.length; i++) {
    hash = (hash * 31 + input.charCodeAt(i)) >>> 0;
  }
  return hash % 100;
}

Le détail important est que hashToPercent est déterministe par utilisateur, et non aléatoire à chaque requête : un utilisateur inclus dans les 5 % doit y rester à chaque requête, sinon l'expérience devient incohérente (la nouvelle interface à un chargement de page, l'ancienne au suivant), ce qui déroute les utilisateurs et rend le débogage impossible à partir d'un rapport de bug.

Là où les flags vont mourir

Le mode d'échec des feature flags n'est pas de les ajouter, c'est de ne jamais les retirer. Un flag à true dans tous les environnements depuis six mois est du poids mort : une branche dont personne ne teste plus le côté false, une condition que chaque futur lecteur doit évaluer mentalement, et une source de « attendez, ce code est-il seulement atteignable ? » pendant le débogage.

Traitez un flag comme une construction temporaire à durée de vie prévue :

  • Nommez-le d'après la fonctionnalité, pas d'après le numéro de ticket (newCheckoutFlow, pas PROJ-4821) : il doit se comprendre de lui-même un an plus tard.
  • Quand un flag est à 100 % en production depuis un moment sans incident, c'est un ticket pour supprimer le flag et l'ancien chemin de code, pas un état permanent.
  • Les flags réellement conçus pour être permanents (un interrupteur d'urgence d'exploitation, un toggle par environnement) forment une autre catégorie : nommez-les et documentez-les autrement pour que personne n'essaie de les « nettoyer ».

Quand les variables d'environnement ne suffisent plus

Les flags basés sur l'environnement atteignent un vrai plafond : en changer un exige encore de toucher à l'environnement de déploiement (un redéploiement, ou au minimum un redémarrage du processus pour prendre en compte la nouvelle valeur), et il n'existe aucun journal d'audit de qui a basculé quoi, et quand. C'est le moment où un service de flags dédié (LaunchDarkly, Unleash, ou une simple table de flags dans votre propre base avec une interface d'administration) mérite sa complexité : basculement instantané sans déploiement, règles par utilisateur sans hachage fait main, et historique des changements.

N'y recourez pas dès le premier jour pour autant. Une douzaine de variables d'environnement lues via un petit module de configuration porteront confortablement un projet pendant longtemps, et migrer plus tard le concept de flag (un nom, un booléen, des sites d'appel qui le vérifient) vers un vrai service de flags est un changement mécanique, pas une refonte.

À retenir

Un feature flag n'est qu'un if dont la condition vit en dehors du code. Centralisez les vérifications, rendez les déploiements par pourcentage déterministes par utilisateur et, la partie que tout le monde saute, supprimez réellement les flags une fois qu'ils se sont stabilisés à 100 %. L'infrastructure pour gérer des flags à grande échelle est facultative ; la discipline pour les retirer ne l'est pas.

Plus récentLa notation Big O expliquée avec du vrai codePlus ancienConstruire un middleware de limitation de débit dans Express