Tips & Tricks (Mis à jour: 02/06/2026)

Feature Flags avec Claude Code : rollouts sûrs, expériences et kill switches

Guide pratique des feature flags avec Claude Code : rollout, expériences, kill switches, ciblage, métriques et cleanup.

Feature Flags avec Claude Code : rollouts sûrs, expériences et kill switches

Commence par la règle d’exploitation, pas par le toggle

Une feature flag est un interrupteur à l’exécution : le code peut être déployé, puis une fonctionnalité peut rester désactivée, être ouverte progressivement ou être coupée en urgence. L’erreur de débutant n’est pas le if (flag). L’erreur coûteuse consiste à mélanger release flag, flag d’expérience et kill switch comme si ces objets avaient la même durée de vie.

Claude Code peut générer très vite une branche d’interface. Une implémentation exploitable demande plus : valeur par défaut sûre, targeting context, séparation serveur/client, journal d’exposition, métriques de guardrail, procédure de rollback et date de suppression des flags temporaires. Dans les workflows de Masa pour un site de contenu ou un petit SaaS, le meilleur prompt n’est pas “ajoute des flags”, mais “qu’est-ce qui peut casser, que peut-on désactiver et comment vérifier que le rollout reste sain”.

Le modèle doit rester aligné avec les sources primaires. OpenFeature sépare l’API d’évaluation côté application du provider utilisé derrière et s’appuie sur un evaluation context pour les données utilisateur, application et environnement. LaunchDarkly documente les usages comme release flags, experiment flags et kill switches. Unleash décrit un cycle Define, Develop, Production, Cleanup, Archived afin d’éviter les flags abandonnées. Les bonnes pratiques Claude Code recommandent aussi de donner une voie de vérification claire.

Références utilisées :

Séparer release, expérience et kill switch

Classe les flags par durée de vie avant d’écrire le code. Une release flag cache une fonctionnalité incomplète, l’ouvre à une audience croissante, puis disparaît après rollout complet. Une flag d’expérience teste une hypothèse et doit enregistrer exposition et résultats. Un kill switch est un contrôle de sécurité plus durable pour panne de fournisseur, explosion de coût, service de recommandation lent ou automatisation risquée.

Cas produitTypeMétrique de succèsAction si échec
Nouveau checkout pour 25 % des comptes ProReleasePaiement terminé, erreurs de paiementCouper checkout_v2_release
Comparer le texte du CTA pricingExpérienceDébuts d’inscription, clics d’intention payanteStopper l’expérience et servir control
Déplacer un bloc affiliate dans le corps d’articleExpérienceClics produit, lecture complèteRevenir au bas de l’article
Désactiver les recommandations lors d’un incident fournisseurKill switchLatence p95, taux 5xxCouper recommendations_enabled

Sans mesure, une flag n’est qu’une supposition. Lis aussi A/B testing avec Claude Code et analytics avec Claude Code. Sur un site monétisé, la CTA ne se limite pas au clic : il faut protéger la qualité AdSense, la lecture complète, le revenu affiliate et l’intention de consultation. Pour démarrer seul, récupère la ressource gratuite ; pour un accompagnement d’équipe, la page consultation en anglais est le bon point d’entrée.

Configuration minimale et evaluator

Ne lie pas trop tôt le code métier à un fournisseur. L’application doit évaluer une flag par key, valeur par défaut et context ; le provider pourra ensuite être LaunchDarkly, Unleash, OpenFeature, un JSON ou un service interne. L’exemple suivant tient dans flag-demo.ts et s’exécute avec npx tsx flag-demo.ts.

type FlagValue = boolean | string | number;
type FlagKind = "release" | "experiment" | "kill_switch";
type Plan = "free" | "pro" | "enterprise";
type Role = "user" | "admin";
type Operator = "equals" | "in";

type FlagContext = {
  targetingKey: string;
  plan: Plan;
  country: string;
  role: Role;
  appVersion: string;
};

type FlagRule = {
  attribute: keyof Omit<FlagContext, "targetingKey">;
  operator: Operator;
  values: string[];
  value: FlagValue;
  percentage?: number;
};

type FlagConfig = {
  key: string;
  kind: FlagKind;
  enabled: boolean;
  defaultValue: FlagValue;
  offValue: FlagValue;
  owner: string;
  removeAfter?: string;
  rules: FlagRule[];
};

const registry: Record<string, FlagConfig> = {
  checkout_v2_release: {
    key: "checkout_v2_release",
    kind: "release",
    enabled: true,
    defaultValue: false,
    offValue: false,
    owner: "growth-platform",
    removeAfter: "2026-07-15",
    rules: [
      {
        attribute: "role",
        operator: "equals",
        values: ["admin"],
        value: true,
      },
      {
        attribute: "plan",
        operator: "in",
        values: ["pro", "enterprise"],
        value: true,
        percentage: 25,
      },
    ],
  },
  pricing_copy_2026_06: {
    key: "pricing_copy_2026_06",
    kind: "experiment",
    enabled: true,
    defaultValue: "control",
    offValue: "control",
    owner: "monetization",
    removeAfter: "2026-06-30",
    rules: [
      {
        attribute: "country",
        operator: "in",
        values: ["JP", "US", "DE"],
        value: "simple",
        percentage: 50,
      },
    ],
  },
  recommendations_enabled: {
    key: "recommendations_enabled",
    kind: "kill_switch",
    enabled: true,
    defaultValue: true,
    offValue: false,
    owner: "sre",
    rules: [],
  },
};

function bucketFor(flagKey: string, targetingKey: string): number {
  const input = `${flagKey}:${targetingKey}`;
  let hash = 0;

  for (const char of input) {
    hash = (hash * 31 + char.charCodeAt(0)) >>> 0;
  }

  return hash % 100;
}

function ruleMatches(
  flagKey: string,
  rule: FlagRule,
  context: FlagContext,
): boolean {
  const actual = String(context[rule.attribute]);
  const matched =
    rule.operator === "equals"
      ? actual === rule.values[0]
      : rule.values.includes(actual);

  if (!matched) return false;
  if (rule.percentage === undefined) return true;

  return bucketFor(flagKey, context.targetingKey) < rule.percentage;
}

export function evaluateFlag<T extends FlagValue = FlagValue>(
  key: string,
  context: FlagContext,
): T {
  const flag = registry[key];
  if (!flag) return false as T;
  if (!flag.enabled) return flag.offValue as T;

  for (const rule of flag.rules) {
    if (ruleMatches(flag.key, rule, context)) {
      return rule.value as T;
    }
  }

  return flag.defaultValue as T;
}

const demoContexts: FlagContext[] = [
  {
    targetingKey: "user_001",
    plan: "pro",
    country: "JP",
    role: "user",
    appVersion: "1.8.0",
  },
  {
    targetingKey: "user_002",
    plan: "free",
    country: "BR",
    role: "admin",
    appVersion: "1.8.0",
  },
];

for (const context of demoContexts) {
  console.log(context.targetingKey, {
    checkout: evaluateFlag<boolean>("checkout_v2_release", context),
    pricingCopy: evaluateFlag<string>("pricing_copy_2026_06", context),
    recommendations: evaluateFlag<boolean>(
      "recommendations_enabled",
      context,
    ),
  });
}

Les détails utiles sont volontairement simples. Une flag inconnue échoue côté sûr, le rollout par pourcentage utilise un targetingKey stable, et chaque flag temporaire porte un owner et un removeAfter. Le registry pourra migrer vers un panneau d’administration sans changer le contrat côté app.

Évaluer côté serveur et afficher côté client

Tout ce qui touche au paiement, aux droits, aux quotas, au stock ou au coût backend doit être évalué côté serveur. Les flags côté client conviennent au texte d’interface, au layout, aux aides d’onboarding ou aux changements visuels déjà autorisés. N’envoie pas les règles secrètes au navigateur et ne remplace jamais l’autorisation par un bouton masqué.

type User = {
  id: string;
  plan: "free" | "pro" | "enterprise";
  role: "user" | "admin";
};

type RequestLike = {
  headers: {
    get(name: string): string | null;
  };
};

export function buildFlagContext(
  user: User,
  request: RequestLike,
): FlagContext {
  return {
    targetingKey: user.id,
    plan: user.plan,
    role: user.role,
    country: request.headers.get("x-country") ?? "US",
    appVersion: process.env.NEXT_PUBLIC_APP_VERSION ?? "dev",
  };
}

export function getServerFlagSnapshot(context: FlagContext) {
  return {
    checkoutV2: evaluateFlag<boolean>("checkout_v2_release", context),
    pricingCopy: evaluateFlag<string>("pricing_copy_2026_06", context),
  };
}
type PricingFlags = {
  pricingCopy: string;
};

export function PricingCta({ flags }: { flags: PricingFlags }) {
  const label =
    flags.pricingCopy === "simple"
      ? "Commencer avec le plan gratuit"
      : "Démarrer l'essai gratuit";

  return <a href="/signup">{label}</a>;
}

Le composant React ne fait qu’afficher un snapshot déjà évalué. Dans le prompt Claude Code, précise que droits et paiement restent côté serveur et que le client ne consomme qu’un résultat pré-évalué.

Déployer avec observabilité

Un rollout sûr ne signifie pas seulement “commencer à 1 %”. Il faut un calendrier de montée, des métriques et des seuils de rollback. Unleash combine pourcentage, stickiness et contraintes. Les guarded rollouts de LaunchDarkly relient le déploiement aux métriques et peuvent déclencher pause ou rollback en cas de régression. Même avec un petit evaluator interne, le modèle opérationnel reste valable.

Suis trois couches : exposition, métrique primaire et guardrails. L’exposition indique qui a vu quelle valeur. La métrique primaire dit si le comportement ciblé progresse. Les guardrails indiquent si tu as dégradé vitesse, erreurs, qualité de revenu, support ou confiance.

type FlagExposure = {
  flagKey: string;
  value: FlagValue;
  targetingKey: string;
  route: string;
  evaluatedAt: string;
};

export function trackFlagExposure(event: FlagExposure) {
  console.log(
    JSON.stringify({
      event_name: "feature_flag_exposure",
      ...event,
    }),
  );
}

Pour un checkout, surveille 5xx, échecs de paiement et tickets support. Pour un blog monétisé, ne regarde pas seulement les clics affiliate : surveille lecture complète, bounce, Core Web Vitals et clics d’intention payante. Pour une fonction IA, mesure coût de tokens, latence p95 et quota par utilisateur. Une hausse de clics avec une baisse de qualité commerciale n’est pas une victoire.

Échecs concrets

Premier échec : répartir au hasard à chaque chargement. Si un refresh passe de A à B, les données d’exposition et de conversion sont inutilisables. Utilise un targeting key stable.

Deuxième échec : cacher une fonctionnalité premium uniquement côté client. Un bouton absent dans React ne protège pas l’API. Une feature flag pilote l’UX, elle ne remplace pas l’autorisation.

Troisième échec : valeur par défaut dangereuse. Une release flag inconnue doit généralement retourner false. Si une faute de frappe retourne true, tu viens de lancer par accident.

Quatrième échec : ne jamais supprimer les flags temporaires. Quelques mois plus tard, checkout_v2_release devient une branche mystérieuse. Dès la décision prise, ouvre le PR de cleanup.

Cinquième échec : trop d’imbrication. Flags parents, flags enfants et pourcentages qui se recouvrent rendent l’audience réelle impossible à expliquer.

Prompts sûrs pour Claude Code

Claude Code peut lire les fichiers, modifier le code et lancer les tests. Donne-lui les limites et la vérification dès le départ.

Ajoute un workflow de feature flags à ce dépôt.
La première flag est checkout_v2_release pour un rollout progressif.

Contraintes:
- Paiement et autorisation sont évalués côté serveur.
- Une release flag inconnue retourne false.
- Le rollout en pourcentage utilise un targetingKey stable.
- Le registry contient owner et removeAfter.
- Ne modifie pas les fichiers non liés.

Sortie attendue:
- Registry minimal et fonction evaluateFlag
- Type d'événement d'exposition
- Au moins 3 cas produit
- Exemples d'échec et étapes de rollback
- Commandes de test exécutées

Avant le merge, utilise un prompt de revue :

Review cette implémentation de feature flags.
Concentre-toi sur defaults, frontière serveur/client,
bucketing stable, événements d'exposition, dates de cleanup
et comportement de rollback. Liste les problèmes par sévérité.

Cette formulation pousse Claude Code vers un système maintenable, pas vers un simple toggle décoratif.

Le cleanup fait partie de la livraison

Chaque flag commence à vieillir le jour de sa création. Les release flags doivent disparaître après rollout complet. Les experiment flags doivent être supprimées après sélection du gagnant. Les kill switches peuvent rester, mais avec owner, runbook et alertes. Ajoute owner, removeAfter, métriques et PR de suppression prévu dans ton template de pull request.

J’ai vérifié l’evaluator de cet article comme démo TypeScript exécutable : le même targetingKey tombe dans le même bucket, les flags inconnues retournent le fallback sûr et le kill switch possède un off value explicite. La note pratique de Masa pour le contenu monétisé : mesurer la qualité en plus des clics. Commence par une release flag, une experiment flag et un kill switch avant de mettre en place une plateforme complète.

#Claude Code #feature flags #rollout sûr #TypeScript #observabilité
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.