Feature flags en Go : comment gérer les releases sans peur ni douleur

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Feature flags en Go : quand chaque ligne de code est un risque

\n\n

Avez-vous déjà été réveillé à 3 heures du matin parce qu'une nouvelle release a « tué » la production ? Ou avez-vous passé des heures à annuler des modifications parce qu'une nouvelle fonctionnalité a cassé la logique de l'ancienne ? Cela vous parle ? Alors les feature flags (drapeaux de fonctionnalités) sont ce qui va radicalement changer votre vie en tant que développeur Go.

\n\n

Les feature flags ne sont pas simplement des « if-else » dans le code. C'est une philosophie de releases sécurisées. C'est la possibilité d'activer et de désactiver des fonctionnalités sans déploiement, de réaliser des tests A/B et de faire des canary releases. Dans le monde de Go, où la vitesse et la fiabilité sont les priorités absolues, les feature flags deviennent un outil incontournable.

\n\n

Que sont les feature flags et pourquoi en avez-vous besoin en Go ?

\n\n

Un feature flag (aussi appelé toggle, drapeau de fonctionnalité) est un mécanisme qui permet d'activer ou de désactiver une fonctionnalité spécifique d'une application sans modifier le code ni redéployer. En termes simples : vous écrivez du code, vous l'enveloppez dans une condition, et si le flag est actif, le code s'exécute ; sinon, il est ignoré.

\n\n

En Go, où la compilation en binaire statique et l'architecture minimaliste sont la norme, les feature flags résolvent plusieurs problèmes à la fois :

\n\n
    \n
  • Réduction du risque des releases — vous avez déployé une fonctionnalité, mais quelque chose s'est mal passé ? Il suffit de désactiver le flag. Sans annulation, sans nouveau déploiement.
  • \n
  • Tests A/B — montrez la nouvelle version à seulement 10 % des utilisateurs, comparez les métriques.
  • \n
  • Canary releases — activation progressive d'une fonctionnalité pour différents groupes d'utilisateurs.
  • \n
  • Dark launches — le code est déjà en production, mais caché. Permet de tester les intégrations sans impact sur les utilisateurs.
  • \n
  • Gestion des dépendances — activez une fonctionnalité uniquement lorsque l'infrastructure backend est prête.
  • \n
\n\n

Comment implémenter les feature flags en Go : du simple au complexe

\n\n

1. La méthode la plus simple : constantes et variables d'environnement

\n\n

Commençons par les bases. L'option la plus primitive, mais fonctionnelle, consiste à utiliser des constantes globales ou des variables d'environnement. Convient pour un MVP ou lorsque la dynamique n'est pas nécessaire.

\n\n
package main\n\nimport (\n    "fmt"\n    "os"\n)\n\nconst NewCheckoutFlowEnabled = false\n\nfunc main() {\n    envFlag := os.Getenv("FEATURE_NEW_PAYMENT")\n    isPaymentEnabled := envFlag == "true"\n\n    if isPaymentEnabled {\n        fmt.Println("Nouvelle passerelle de paiement active")\n        processNewPayment()\n    } else {\n        fmt.Println("Ancienne passerelle de paiement utilisée")\n        processOldPayment()\n    }\n}\n\nfunc processNewPayment() {\n    fmt.Println("Traitement via Stripe API v3")\n}\n\nfunc processOldPayment() {\n    fmt.Println("Traitement via Stripe API v2")\n}
\n\n

Les inconvénients sont évidents : pas de gestion dynamique, nécessité de redémarrer l'application. Mais pour des scénarios simples, cela convient.

\n\n

2. Approche structurée : votre propre gestionnaire de flags

\n\n

Lorsque le nombre de flags dépasse 5, il est temps d'extraire la logique dans un package séparé. Créons un gestionnaire de feature flags simple avec un stockage en mémoire.

\n\n
package feature\n\nimport (\n    "sync"\n    "time"\n)\n\ntype Flag struct {\n    Name      string\n    Enabled   bool\n    UpdatedAt time.Time\n}\n\ntype Manager struct {\n    mu    sync.RWMutex\n    flags map[string]Flag\n}\n\nfunc NewManager() *Manager {\n    return &Manager{\n        flags: make(map[string]Flag),\n    }\n}\n\nfunc (m *Manager) IsEnabled(name string) bool {\n    m.mu.RLock()\n    defer m.mu.RUnlock()\n    flag, exists := m.flags[name]\n    return exists && flag.Enabled\n}\n\nfunc (m *Manager) Enable(name string) {\n    m.mu.Lock()\n    defer m.mu.Unlock()\n    m.flags[name] = Flag{\n        Name:      name,\n        Enabled:   true,\n        UpdatedAt: time.Now(),\n    }\n}\n\nfunc (m *Manager) Disable(name string) {\n    m.mu.Lock()\n    defer m.mu.Unlock()\n    m.flags[name] = Flag{\n        Name:      name,\n        Enabled:   false,\n        UpdatedAt:                

Blogs

Book Recommendations