Continuous deployment : quelques patterns

Feature Flipping

Le code en cours de développement est livré en production, mais son exécution est conditionnée à la présence d’un flag qui peut être activé ou non par configuration, sans nécessiter un redéploiement.

Ce mécanisme peut se mettre en oeuvre de façon simple dans le code, avec un helper chargé de vérifier l’activation de la fonctionnalité au niveau de la configuration :

if (FeatureFlippingHelper.isFeatureEnabled("myFeature")) {
   // code de la nouvelle fonctionnalité
} else {
   // code du comportement actuel
}

Cette approche permet de limiter les risques liés au feature branching en éliminant les problèmes de merge de code. Elle rend également la mise à disposition d’une fonctionnalité indépendante de sa mise en production. En cas de problème relatif à cette fonctionnalité, il est également très facile de la désactiver le temps de régler le problème, sans mettre en péril toute l’application.

Il existe toutefois des inconvénients :

  • le Feature Flipping se prête bien aux langages de programmation, beaucoup moins aux langages de description tels que XML pour lesquels on est tenté de fournir plusieurs versions différentes
  • le code applicatif se retrouve encombré par le code lié au Feature Flipping, et devient moins lisible, en particulier lorsqu’une portion de code est impactée par différentes fonctionnalités nouvelles
  • le coût des tests augmente : il faut tester l’application dans les deux cas, fonctionnalité activée ou non
  • une fois la fonctionnalité livrée, il convient de nettoyer le code pour éliminer le Feature Flipping qui lui est relatif

Le Feature Flipping met en oeuvre la capacité à identifier des fonctionnalités précises au niveau du code. Il est tout à fait possible d’enrichir la mécanique déterminant l’activation de la fonctionnalité avec des éléments du contexte applicatif, tels que l’utilisateur connecté, les droits qui lui sont rattachés ou encore des paramètres régionaux. Dès lors, on peut étendre ce pattern à d’autres usages comme restreindre la fonctionnalité à certaines catégories d’utilisateurs : livraison de fonctionnalités en avant-première, limitées à certains rôles ou dépendant d’un niveau d’offre commerciale (fonctionnalités premium en fonction de la licence), ou encore de choix effectués par l’utilisateur sur son profil.

Blue/Green Deployment

Ce pattern est essentiellement mis en oeuvre dans le cadre du déploiement sans interruption (ou zero-downtime deployment) qui veille à maintenir la disponibilité des applications pendant la livraison.

Il nécessite que l’application soit déployée de façon répartie sur plusieurs chaînes applicatives, avec une répartition de charge (load balancing). Lors de la livraison, on interrompt une des chaînes applicatives pour livrer la nouvelle version (chaîne green), tandis que les autres chaînes sont disponibles avec la version précédente (chaîne blue). Ensuite, on remplace progressivement les autres chaînes applicatives avec la nouvelle version.

Du point de vue extérieur, la livraison n’arrête pas l’application, on peut juste s’attendre à davantage de charge sur les serveurs en cours de fonctionnement.

Une variante de ce pattern est le Canary Release : on donne accès à la version N+1 à un ensemble réduit d’utilisateur, tandis que les autres restent en version N. Si tout se passe bien, l’ensemble des utilisateurs passera en version N+1.

Migration de base de données

La mise en oeuvre d’une nouvelle version entraîne parfois des changements dans le schéma de base de données, avec parfois des incompatibilités. L’ajout de tables ou de colonnes ne pose en général pas de problèmes, la suppression non plus parce qu’il suffit de différer ces opérations après la livraison de l’application.

Les modifications de structure (par exemple éclater une colonne sur une nouvelle table) entraînent plus de complexité, la difficulté est alors de faire cohabiter les 2 versions avec un unique schéma dit transitoire, mais dans lequel les 2 versions des données modifiées cohabitent.

Cela nécessite des efforts côté base de données mais aussi côté code applicatif.

Pour la base de données, il faut prévoir :

  • une phase de transition, dans laquelle deux versions de la donnée transformée coexistent
  • une phase de consolidation, dans laquelle on aboutit au nouveau schéma
  • une possibilité de retour arrière avec retour au schéma initial sans perte de données

Pour le code applicatif, il faut :

  • s’assurer de la cohérence des données entre leur deux versions
  • pouvoir accéder à l’une ou l’autre version de façon transparente (ceci peut se gérer par une forme de Feature Flipping)
  • ce code doit être livré avant la phase de transition

Laisser un commentaire