Des feature flags sans déploiement

Engineering

Certaines valeurs de votre code n’ont rien à y faire.

Le texte de la bannière de la page d’accueil. Le pourcentage de remise de la promo d’été. Le flag qui cache un tunnel de paiement à moitié fini à tout le monde sauf à votre équipe. Chacun de ces changements tient en une ligne et coûte une pull request, une revue, un passage de CI et un déploiement. Et la personne qui veut ce changement ne peut généralement pas le faire elle-même.

Le module Configuration est un stockage clé-valeur, et c’est tout l’intérêt

Le module Configuration de Kookee stocke sous une clé n’importe quelle valeur sérialisable en JSON, et vous la renvoie par le même SDK que le reste de votre contenu :

TypeScript
const flag = await kookee.config.getByKey('feature-new-checkout');

if (flag.value === true) {
  // nouveau tunnel de paiement
}

Les valeurs ne se limitent pas aux booléens, et c’est là que ça devient vraiment utile. Toute une configuration d’interface peut tenir sous une seule clé :

TypeScript
const banner = await kookee.config.getByKey('homepage-banner');
// { text: 'Soldes d’été !', enabled: true, color: '#ff6b00' }

Ou une règle métier que votre équipe finance veut gérer elle-même :

TypeScript
const discount = await kookee.config.getByKey('pricing-discount');
// { percentage: 20, minOrderValue: 50 }

Récupérer ce qu’il faut en un seul appel

Récupérer les clés une par une à chaque chargement de page, c’est l’erreur évidente. list() accepte un filtre par clés :

TypeScript
const configs = await kookee.config.list({
  keys: ['homepage-banner', 'feature-new-checkout', 'pricing-discount'],
});

Une requête, trois valeurs. Récupérez-les une fois par rendu côté serveur, ou une fois au démarrage de l’application côté client, puis transmettez-les aux composants qui en ont besoin. Chaque lecture de configuration est un appel d’API décompté de votre quota mensuel de requêtes : avec le forfait gratuit, c’est 100 000 requêtes par mois. Ici, la mise en cache n’est pas une optimisation prématurée, c’est l’usage normal.

L’historique des versions sert de filet de sécurité

Modifier en production n’est rassurant que si les erreurs coûtent peu. Dans Kookee, chaque modification d’une configuration crée un instantané, et toute version des 30 derniers jours se restaure en un clic.

Cela change le niveau de risque quand on confie une clé à quelqu’un qui n’est pas ingénieur. Dans le pire des cas, une remise mal saisie ne déclenche pas un incident avec un correctif déployé en urgence. Elle se règle par un retour en arrière dans le tableau de bord, fait par la personne même qui a modifié la valeur, dans les dix secondes après qu’elle s’en est aperçue.

Pour la même raison, les descriptions comptent plus qu’il n’y paraît. enable-v2 ne veut rien dire pour la personne qui en héritera dans six mois. Rédigez la description au moment où vous créez la clé.

Les limites du module Configuration

Le module Configuration est volontairement simple : une clé, une valeur et un historique. Il ne fait ni déploiement progressif par pourcentage, ni ciblage par utilisateur, ni répartition A/B. Si vous devez montrer une fonctionnalité à 5 % des utilisateurs en Allemagne, il vous faut une plateforme d’expérimentation dédiée, et c’est elle qu’il faut utiliser.

Ce que couvre le module Configuration, c’est le cas bien plus courant : une valeur globale que votre équipe doit modifier sans livrer de code. C’est le cas de la plupart des flags, la plupart du temps. Et le flag vit alors à côté de vos articles de blog, de vos articles d’aide et de votre journal des modifications, derrière la même clé API, au lieu d’ajouter un quatrième fournisseur à votre stack.

Une approche raisonnable consiste à garder une valeur par défaut dans le code et à laisser le module Configuration la remplacer :

TypeScript
let showNewCheckout = false;

try {
  const flag = await kookee.config.getByKey('feature-new-checkout');
  showNewCheckout = flag.value === true;
} catch {
  // coupure réseau : on garde la valeur par défaut sûre
}

Le flag est désormais distant, et si l’API est injoignable, l’application retombe sur le dernier comportement qui a fait ses preuves au lieu de lever une exception sur la page de paiement.