Automation

Azure App Configuration : diagnostiquer la dérive d'un feature flag avant rollback

Un runbook de production pour qualifier des évaluations de feature flag incohérentes entre instances en séparant version, refresh, cache, ciblage, télémétrie et rollback.

28 août 2026 azureapp-configurationfeature-flagsautomationobservabilityapplication-insightskqlcacherolloutrunbookrollbackproduction

Un feature flag CheckoutV2 est passé de 10 % à 25 % en production. Quelques minutes plus tard, certains utilisateurs voient encore l’ancien parcours, d’autres changent de variante entre deux requêtes et une partie des instances ne semble jamais recevoir la nouvelle allocation. Désactiver immédiatement le flag peut contenir l’impact, mais ne dit pas si la panne vient de la définition publiée, du refresh applicatif, du contexte de ciblage ou de la télémétrie.

Ce runbook traite le flag comme une dépendance de production. L’objectif est de prouver l’état demandé, l’état chargé et l’état réellement évalué avant de choisir entre maintien, correction du refresh, réduction du rollout ou rollback.

Figer le changement et le symptôme

Commencez par une fiche qui relie le changement à une fenêtre UTC, un label et une population affectée. Un nom de flag seul est ambigu dès que plusieurs environnements, labels ou variantes existent.

yaml feature-flag-incident.yml
incident:
flag: CheckoutV2
store: <app-configuration-store>
label: prod
changed_at_utc: 2026-08-28T05:40:00Z
change: rollout_10_to_25_percent
expected_variant: checkout-v2
previous_known_good: rollout_10_percent

scope:
services:
  - web-frontend
  - checkout-api
regions:
  - westeurope
  - northeurope
symptom:
  - inconsistent variant between requests
  - instances still evaluating the previous allocation

stop_conditions:
- payment error rate exceeds threshold
- assignment changes for the same targeting ID
- flag state cannot be tied to an ETag
- rollback state is not known

Relevez aussi le dernier déploiement applicatif, la version des bibliothèques App Configuration et feature management, ainsi que tout changement d’identité, de réseau ou de réplica. La simultanéité ne prouve pas la cause, mais elle borne les comparaisons utiles.

Prouver la définition publiée

Lisez la clé exacte associée au flag et à son label. Conservez sa valeur brute, son ETag et son horodatage comme preuve. Ne vous contentez pas de la vue du portail ou d’une capture d’écran sans contexte.

bash capture-feature-flag.sh
set -euo pipefail

store="<app-configuration-store>"
flag="CheckoutV2"
label="prod"
key=".appconfig.featureflag/$flag"

az appconfig kv show --name "$store" --key "$key" --label "$label" --auth-mode login --output json > feature-flag-current.json

jq '{key, label, etag, last_modified, content_type, value}' feature-flag-current.json

Vérifiez que la définition correspond au changement attendu : état activé, variantes, pourcentages, groupes, utilisateurs, fenêtre temporelle et seed éventuel. Une allocation valide sur le plan JSON peut rester incorrecte sur le plan opérationnel si le mauvais label a été modifié ou si un override utilisateur prend le pas sur le pourcentage.

Comparez ensuite avec l’état connu avant changement. Si cette référence n’existe que dans une conversation ou un ticket, le rollback n’est pas encore exécutable. Préparez une définition restaurable et faites-la relire avant toute mutation.

Séparer publication, chargement et évaluation

Un feature flag traverse trois états distincts :

  • l’état publié dans App Configuration ;
  • l’état chargé et mis en cache par chaque processus ;
  • le résultat évalué pour un contexte utilisateur donné.

Une définition correcte dans le store ne prouve donc pas que toutes les instances la consomment. À l’inverse, deux utilisateurs peuvent recevoir des variantes différentes sans dérive si le ciblage le prévoit.

Exposez temporairement un diagnostic en lecture seule qui ne retourne ni donnée sensible ni liste complète de ciblage. Il doit permettre de comparer les instances sans transformer l’application en console de configuration.

json feature-flag-diagnostic.json
{
"service": "checkout-api",
"region": "westeurope",
"instance": "checkout-api-7f8c9",
"deploymentVersion": "2026.08.28.1",
"flag": "CheckoutV2",
"label": "prod",
"configurationEtag": "<etag>",
"lastRefreshUtc": "2026-08-28T05:42:18Z",
"evaluation": {
  "targetingIdHash": "<stable-hash>",
  "enabled": true,
  "variant": "checkout-v2",
  "assignmentReason": "Percentile"
}
}

Comparez au moins une instance saine et une instance affectée dans chaque région. Si l’ETag diverge, cherchez le refresh ou l’accès au store. Si l’ETag est identique mais le résultat diffère pour le même contexte stable, examinez le ciblage, le seed et la construction du contexte. Si l’état change au milieu d’une même requête, vérifiez que l’application utilise un snapshot de feature manager lorsque cette cohérence est requise.

Diagnostiquer le refresh sans provoquer un second incident

Le refresh dépend du provider et du framework utilisés. Documentez le mécanisme réel : intervalle, déclencheur par requête, middleware, sentinel éventuel et comportement lorsque App Configuration est indisponible.

Contrôlez les points suivants :

  • toutes les instances chargent le même endpoint, label et filtre de clés ;
  • le chemin qui déclenche le refresh est réellement traversé ;
  • l’intervalle n’est pas confondu avec une garantie de mise à jour immédiate ;
  • les erreurs 401, 403, 429, DNS ou TLS vers App Configuration sont visibles ;
  • l’application garde un dernier état connu plutôt que de basculer silencieusement vers une valeur par défaut dangereuse ;
  • un éventuel sentinel est modifié après les clés qui composent le changement, pas avant.

Ne redémarrez pas toutes les instances pour « vider le cache ». Cette action détruit la preuve du comportement courant et peut simultanément charger une mauvaise définition partout. Utilisez d’abord une instance canari, forcez un refresh par le mécanisme supporté par l’application, puis comparez ETag et évaluation.

Corréler les évaluations dans Application Insights

La télémétrie du flag doit répondre à deux questions : quelle définition a été évaluée et quel résultat a été servi ? Lorsqu’elle est activée, l’événement FeatureEvaluation peut porter notamment le nom du flag, l’état, la variante, la raison d’affectation, l’ETag et l’identifiant d’allocation.

kusto feature-flag-drift.kql
customEvents
| where timestamp between (datetime(2026-08-28T05:20:00Z) .. datetime(2026-08-28T06:20:00Z))
| where name == "FeatureEvaluation"
| extend Flag = tostring(customDimensions.FeatureName),
       Enabled = tobool(customDimensions.Enabled),
       Variant = tostring(customDimensions.Variant),
       Reason = tostring(customDimensions.VariantAssignmentReason),
       ETag = tostring(customDimensions.ETag),
       AllocationId = tostring(customDimensions.AllocationID),
       Role = cloud_RoleName,
       Instance = cloud_RoleInstance
| where Flag == "CheckoutV2"
| summarize Evaluations=count(),
          Instances=dcount(Instance),
          Etags=make_set(ETag, 10)
by bin(timestamp, 5m), Role, Enabled, Variant, Reason, AllocationId
| order by timestamp asc

Adaptez les noms de dimensions au schéma réellement émis. L’absence d’événement n’est pas la preuve que le flag n’a pas été évalué : elle peut révéler une télémétrie désactivée, filtrée ou échantillonnée. Corrélez avec les traces de refresh, la version du service, la région et les métriques métier. N’enregistrez pas d’identifiant utilisateur brut si un identifiant pseudonymisé stable suffit.

Décider maintien, correction ou rollback

text feature-flag-decision.txt
MAINTENIR
La définition, les ETag et les allocations correspondent au rollout.
Les écarts observés sont expliqués par le ciblage attendu.
Les métriques techniques et métier restent dans la fenêtre validée.

CORRIGER LE REFRESH
Le store porte la bonne définition, mais certaines instances gardent un ancien ETag.
Corriger le déclencheur ou le filtre sur une instance canari.
Étendre seulement après convergence prouvée.

RÉDUIRE LE ROLLOUT
La définition est cohérente, mais la variante dégrade les métriques.
Revenir au pourcentage connu, conserver la même population de contrôle
et vérifier la nouvelle allocation avant de poursuivre.

ROLLBACK
Restaurer la définition connue et relue avec une écriture conditionnelle.
Prouver le nouvel ETag, attendre la convergence des instances,
puis vérifier évaluations et métriques métier.

STOPPER ET INVESTIGUER
L'état précédent est inconnu, plusieurs labels ont changé,
ou les instances ne permettent pas de relier évaluation et version.

Évitez le rollback aveugle par écrasement. Entre la collecte et l’action, un autre opérateur peut avoir publié une correction. Utilisez une précondition fondée sur l’ETag ou le mécanisme de concurrence supporté par votre chaîne, journalisez l’acteur et conservez la définition remplacée.

Après rollback, la validation n’est pas « le portail affiche 10 % ». Elle exige que les instances convergent vers l’ETag restauré, que les affectations redeviennent stables et que les métriques affectées reviennent dans la fenêtre attendue. Gardez ensuite un canari synthétique qui évalue le flag avec des contextes fixes avant chaque augmentation du rollout.

Conclusion

Une dérive de feature flag ne se résout pas toujours en basculant un interrupteur. Il faut relier la définition publiée, l’ETag chargé par chaque instance, le contexte de ciblage et le résultat servi. Cette chaîne distingue un rollout normal d’un cache obsolète, d’un label incorrect ou d’une vraie régression fonctionnelle.

La décision devient alors vérifiable : maintenir une allocation expliquée, corriger le refresh sur un canari, réduire progressivement l’exposition ou restaurer l’état connu avec contrôle de concurrence. Le rollback n’est terminé que lorsque les instances et les métriques prouvent la convergence.