Stratégies de branching pour CI/CD

En écho à l'éternel conflit opposant les partisan·es des tabulations et aux partisan·es des espaces, les stratégies de branching suscitent des débats passionnés tant en ligne que hors ligne.

De nombreuses opinions bien arrêtées s'affrontent sur la meilleure approche, mais comme pour tant d'autres problématiques du développement logiciel, la bonne réponse dépend du contexte. Sur ces bonnes paroles, détaillons un peu plus les stratégies de branching et les modalités de leur intégration à la CI/CD.

En résumé, une stratégie de branching concrétise le consensus établi dans votre équipe pour déterminer la manière et le moment de créer et de fusionner des branches dans le contrôle des versions. La configuration de votre système de contrôle des versions et votre utilisation des branches aura un impact sur la configuration de votre pipeline CI/CD, il est donc important de choisir un modèle adapté à vos besoins.

Les branches dans le DVCS

Les stratégies de branching sont entrées dans les points à prendre en compte pour les équipes de développement avec le gain de popularité des systèmes de contrôle de version distribués (et Git en particulier), qui ont facilité le branching.

Avec les systèmes distribués, il existe plusieurs copies du référentiel et donc plusieurs sources de vérité (même s'il est courant que les équipes désignent une copie centrale ou principale). Vous et les membres de votre équipe travaillez sur vos modifications en parallèle sur vos copies du dépôt, et partagez votre travail en envoyant les modifications vers d'autres copies du même dépôt en mode push.

Git a incroyablement simplifié la création de branches et la fusion des commits de différentes branches. Dans Git, une branche est simplement un commit (ou un ensemble de commits) étiqueté avec un nom particulier. Les branches sont idéales pour développer un ensemble de modifications, comme un travail sur une nouvelle fonctionnalité ou une expérience improvisée.

Vous pouvez ensuite partager vos modifications en envoyant la branche vers un autre dépôt en mode push, ou en la fusionnant avec une autre branche (notamment la branche de référence) pour la combiner avec le reste de la base de code. Vous pouvez également rejeter les modifications si vous n'êtes pas convaincu·e. Lorsque vous fusionnez deux branches ensemble, Git se charge d'aligner les commits sur chacune d'elles et évite une grande partie de la complexité associée à la fusion dans d'autres systèmes de contrôle des versions.

Utilisation des branches pour la CI/CD

L'intégration continue cherche à inciter tous les membres de votre équipe de développement à valider régulièrement leurs modifications. Vous pouvez ainsi vérifier régulièrement que tout fonctionne comme prévu, plutôt que de passer des semaines ou des mois à essayer d'intégrer différents flux de travail une fois le codage terminé. Cela permet en outre de publier plus souvent des mises à jour logicielles, et donc de profiter des avantages de la livraison et du déploiement continus.

Les décisions concernant l'emplacement où valider les modifications, le moment où les builds et les tests automatisés doivent être lancés et l'emplacement depuis lequel publier les mises à jour sont au carrefour d'interaction entre l'utilisation des branches et la CI/CD. L'approche la plus simple (du moins sur le plan conceptuel) consiste à éviter complètement les branches. Dans le cadre d'un développement axé sur le tronc, tout le monde apporte régulièrement des modifications à la branche de référence d'un dépôt central, maintenu dans un état libérable et fréquemment déployé en production.

Bien que le développement axé sur le tronc puisse être très efficace, en particulier si vous disposez d'une configuration CI/CD mature et que vous effectuez un déploiement continu sur un système hébergé, il présente également certains défis. Les stratégies de branching offrent des moyens alternatifs de gérer les modifications du code, avec divers avantages et inconvénients. Voici certaines des plus utilisées par les équipes de développement utilisant la CI/CD.

Branches de fonction

Comme leur nom l'indique, les branches de fonctionnalités sont créées pour séparer les fonctionnalités individuelles du reste de la base de code. Maintenir les travaux en cours en dehors de la branche de référence peut faciliter le maintien de cette version de référence dans l'état déployable et (si vous déployez directement à partir de la version de référence plutôt qu'à partir d'une branche de publication, voir ci-dessous) éviter le risque que des fonctionnalités inachevées soient déployées en production. Vous pouvez toujours partager votre travail avec le reste de votre équipe en envoyant votre branche par push soit vers le dépôt central (si vous en avez désigné un), soit vers un autre dépôt au choix.

Le principal inconvénient des branches de fonctionnalités (et la critique qui leur est souvent adressée par les adeptes du développement axé sur le tronc) est qu'en retardant l'intégration des modifications jusqu'à ce que la fonctionnalité soit « terminée », vous perdez les avantages de l'intégration continue. Vous risquez de provoquer des conflits de fusion et d'introduire des bogues plus complexes qui prennent plus de temps à corriger que si les modifications avaient été apportées de manière itérative.

Vous pouvez atténuer ces problèmes en configurant votre serveur CI pour qu'il exécute des builds et des tests automatisés sur les branches de fonctionnalités ainsi que sur la version de référence (ou toute autre branche que vous utilisez pour préparer les sorties officielles). Cela permet de bénéficier d'un retour d'expérience immédiat sur le travail en cours, tout en réduisant le risque de problèmes lors de la fusion des modifications.

Pour minimiser les conflits de fusion, vous pouvez vous assurer que les branches de fonctionnalités aient une durée de vie courte, d'un jour ou deux tout au plus. Une autre option consiste à rebaser la branche par-dessus la branche de référence ou à fusionner régulièrement les modifications à partir de cette branche. Cela actualise votre branche de fonctionnalité avec toutes les autres modifications validées dans la version de référence. Toutefois, si vous avez un grand nombre de branches de fonctionnalités concurrentes qui retardent les commits vers la version de référence, le risque de conflits perdure.

Branches de publication

Alors que les branches de fonctionnalité permettent de gérer les travaux en cours, les branches de publication sont utilisées pour « renforcer » les modifications avant publication. Les branches de publication conviennent bien à un modèle de livraison continue, où les mises à jour sont livrées à intervalles réguliers plutôt que dès qu'elles sont prêtes. Elles facilitent en outre la prise en charge de plusieurs versions en production.

Avec un workflow de branche de publication, une fois que les modifications prévues pour une version donnée sont prêtes, une branche de publication contenant les changements pertinents est créée (soit à partir de la version de référence, soit à partir d'une autre branche de développement). Après cela, aucune autre fonctionnalité n'est fusionnée. Pendant ce temps, le développement des fonctionnalités prévues pour d'autres versions peut continuer à être fusionné avec la version de référence ou ailleurs.

Les builds de la branche de publication sont ensuite soumises à une série de tests automatisés, et les corrections de bogues sont apportées à la branche de publication. S'ensuivent d'autres séries de tests jusqu'à ce que vous soyez prêt·es à publier le produit. Ces corrections de bogues doivent également être appliquées à la branche de référence pour garantir leur inclusion dans les futures versions du produit.

Si vous conservez vos branches de publication aussi longtemps que vous avez besoin d'assurer l'assistance de chaque version de votre logiciel, il est beaucoup plus facile de déployer des correctifs pour les anciennes versions.

Lorsqu'une mise à jour est nécessaire, elle peut être développée dans une branche de correctif rapide ou sur la version de référence, et testée normalement, puis appliquée à la branche de publication (par exemple, via une sélection précise).

Elle sera ensuite exécutée par le pipeline CI/CD sur cette branche pour garantir l'absence de problèmes sur cette version avant de déployer le changement. Vous pouvez également effectuer directement le travail sur la branche de publication et le réappliquer sur la version de référence le cas échéant.

Branches de correctif rapide

Les branches de correctif rapide ont un fonctionnement similaire à celui des branches de fonctionnalités, mais il est utile de les mentionner pour boucler le tour d'horizon. Une branche de correctif rapide a pour but de passer une correction de bogue en production aussi rapidement que possible.

Vous pouvez créer une branche de correctif rapide à partir de la branche de référence ou d'une branche de publication, selon l'emplacement où vous souhaitez la déployer. Comme pour les branches de fonctionnalités, vous pouvez choisir d'exécuter certains des éléments de CI/CD sur votre branche de correctif rapide avant de la fusionner avec la branche de publication ou la branche de référence, où elle peut subir d'autres tests automatisés avant sa publication.

Conclusion

Nous avons examiné quelques-unes des utilisations les plus courantes des branches dans votre workflow de CI/CD, mais de nombreuses autres variantes existent.

L'essentiel est de trouver une stratégie qui fonctionne dans votre contexte spécifique. En adoptant une mentalité DevOps d'amélioration continue et en restant ouverte aux diverses possibilités, vous pourrez continuer à adapter votre approche à vos besoins au fil de la maturation de votre pratique CI/CD.