tominardi.fr

Git Flow - Mon opinion en 2024

15 janvier 2024

J'entends de plus en plus de personnes me dire qu'ils abandonnent git flow pour passer sur d'autres modèles de gestion de branche. Certains allant même jusqu'à dire que sa pratique est déconseillée. La plupart du temps, je ne suis pas convaincu par les explications données. Rapide article d'opinion.

Pourquoi Git Flow ?

Git Flow, aussi orthographié gitflow, est une stratégie d'utilisation de gestion de branches pour Git qui a connu son pic de popularité il y a sans doute déjà une dizaine d'années.

Le modèle est basé sur 2 branches : la branche master, qui contient le code "actuellement en production", et la branche develop (dont vous pouvez personnaliser le nom), qui est la dernière version à jour et fonctionnelle du logiciel. Pour chaque feature qu'on ajoutera dans le code, on fera une branche spécifique qui est destinée à être fusionnée dans develop, une fois qu'elle est terminée. Pour chaque correction urgente à faire en production, on créera une branche hotfix à partir de master, branche qu'on fusionnera à la fois dans master et dans develop.

L'objectif de gitflow est de faire des montées de version sécurisée avec du code testé, tout en se donnant la possibilité de corriger rapidement des bugs en production. Ses avantages sont multiples, allant de la possibilité de tester un incrément produit sur une branche séparée (en vue d'obtenir une validation QA et/ou de la relecture de code, par exemple), à la gestion fine et propre du journal des modifications grâce à une réécriture de l'historique facilité, en passant par un travail en équipe censé être facilité par les branches de features.

Pourquoi c'est critiqué ?

Comme je l'ai dit, le modèle est critiqué depuis quelques années. Certains lui préfèrent des systèmes de gestion alternatifs, et, bien qu'il existe beaucoup de modèles de gestion de branches différents, j'ai le sentiment que les modèles trunk based (c'est-à-dire basés sur des développements réalisés directement sur une branche master) sont ceux qui ont la faveur des développeurs ces dernières années (source tominardimètrique 2024). Ces modèles imposent que tout le code livré soit en permanence fonctionnel, et encouragent donc l'application de pratiques d'excellence en matière de développement logiciel, ce qui est une bonne chose.

En comparaison avec ces modèles, les développeurs remontent souvent quelques difficultés récurrentes avec leur utilisation de git flow, qui seraient atténuées avec un trunk based :

Gestion complexe des branches de fonctionnalités

Certains estiment que la multiplication des branches de fonctionnalités complique la gestion, surtout à mesure que l'équipe s'agrandit. Récupérer quotidiennement le code commun sur sa feature branch est source de problèmes récurrents, surtout s'il y a eu beaucoup de modifications sur le tronc commun.

Problèmes liés aux merges

Les merges pour livrer une version sont parfois considérés comme cauchemardesques, et l'historique Git peut être encombré de commits de merge.

Difficulté à maintenir un flux continue

La livraison continue peut être entravée, et des temps d'attente pour récupérer la dernière version du code peuvent bloquer le travail des développeurs. De plus, j'ai régulièrement entendu que l'équipe avait besoin de pouvoir corriger le code en production "tout de suite", sans devoir passer par une livraison de version via le système de release.

Mon point de vue sur ces critiques

Gestion des branches de fonctionnalité

Selon moi, le nœud du problème réside dans le fait que beaucoup de personnes n'aient pas complètement intégré que les branches de fonctionnalité ne sont qu'éphémères. Leur durée de vie doit se compter en heure plutôt qu'en jours. Il est crucial de comprendre que les "features" dans Git Flow ne correspondent pas nécessairement à des fonctionnalités métier, mais plutôt à des incréments logiciels. Pour une "feature" d'un point de vue Product Owner, on ne fait pas une seule branche, mais une multitude de branches pour chaque petit incrément.

Ainsi, il n'est ni question de multiplier ces branches de fonctionnalité (on doit en avoir une par développeur au maximum, temps de fusion de branche mis à part), ni de les maintenir sur le moyen terme.

Une fois qu'on a ça en tête, la grosse majorité des problèmes disparaissent. Les feature branchs sont juste un moyen pour livrer le nouveau code, au jour le jour, pour gérer au mieux la gestion des conflits et la préparation de ses commits. Mais globalement, on code sur develop.

Au passage, j'ai toujours trouvé le terme "feature branch" trompeur.

Concernant les branches de release, c'est encore plus radical : en git flow, une branche de release est créée uniquement dans le but de mettre à jour la branche master avec le code de develop. C'est tellement éphémère qu'une branche de release n'est presque jamais poussé sur le dépôt distant, elle est juste là pour permettre de faire le petit commit de montée de version, vérifier deux trois trucs et merger directement. J'ai déjà vu des organisations maintenir pendant quelques jours, voir quelques semaines des releases branches pour préparer leur livraison dessus : c'est une erreur. La version "prête pour prod" est, et doit toujours être, develop. La release branch n'est là que pour gérer le processus de merge.

Gestion des merges et des conflits

La récupération de la dernière version de develop sur sa feature branch devrait être un évènement assez rare, si la durée de vie de la branche est gardée courte. Basiquement, on fera ça juste avant de fusionner son code dans develop ou de créer la Pull Request, selon la politique de l'équipe.

Comme je l'ai dit, la feature branch est personnelle. Cela permet quelque chose d'assez formidable : on peut écrire l'historique qu'on veut là-dessus. On peut faire des git push -f à l'envi. Du coup, je pense que la meilleure stratégie à adopter pour récupérer le code du tronc commun est le bon vieux rebase.

Ainsi, le développeur règle ses conflits comme un grand avant de proposer son code.

Comme une feature branch est un petit incrément, normalement la tâche sera très simple localement.

Livraison en continu avec git flow

La possibilité de livrer des correctifs en production rapidement, sans nécessiter la livraison de l'ensemble du code en cours, est un avantage significatif. Je n'ai jamais compris l'argument "j'ai besoin de livrer du code en production n'importe quand, git flow est un obstacle pour ça", alors que la stratégie de hotfix répond parfaitement à cette problématique. En comparaison, un dépôt trunk based ne permettra pas facilement de pousser un correctif sans embarquer les dernières features qu'on ne veut pas forcément livrer (avec, par exemple, une lourde migration à prévoir lors de la MEP, ou de nouvelles dépendances).

De plus, pour que les branches de fonctionnalité gardent un cycle de vie très court, on va être amené à fusionner du code "inachevé", dans le sens où la fonctionnalité métier n'est pas pleinement utilisable. Ce problème peut être résolu en utilisant des mécanismes tels que le "feature flipping" (ou "feature flags"). Ainsi, dans la mesure où develop est nécessairement une branche stable, il est possible de la livrer en production de manière continue. Par exemple, lors de chaque build en succès, ou bien quotidiennement.

Le problème des temps d'attente

Certaines pratiques du travail en équipe, en particulier la mise en place des Pull Request, peuvent entrer en collision avec la pratique de git flow, dès lors que l'équipe qui travaille sur le même dépôt grossie un peu.

Ma tâche B dépend de la tâche A, mais celle-ci est toujours en review. Qu'est-ce que je fais pour ne pas être bloqué ? Je crée une nouvelle branche à partir de la feature branch ?

Cette situation, finalement assez commune, concerne les temps d'attente entre le moment où on propose le code et le moment où il est effectivement accepté dans le tronc commun. Ce laps de temps va dépendre des pratiques de l'organisation, et ne sera pas sans poser de problèmes. En effet, les membres de l'équipe autorisés à fusionner le code sur le tronc commun sont parfois beaucoup moins nombreux que l'équipe. Ils vont alors avoir tendance à mutualiser leur temps pour traiter plusieurs "pull requests" d'un coup. Dans ces situations, la première branche fusionnée va potentiellement entrainer des conflits avec toutes ou partie des branches restantes, obligeant un ou plusieurs développeurs à repasser sur leurs branches.

J'ai tendance à penser que c'est un problème de pratiques, et non un problème avec git flow en lui-même. En effet, Git Flow n'a pas rapport avec les politiques de Pull Request, de relecture de code, de CI, etc. Si certains process prennent trop de temps et occasionnent des bouchons de manière très récurrente, alors peut-être qu'il faut adapter sa stratégie sans forcément remettre en cause le flow.

Selon moi, il ne devrait pas se passer plus d'une demi-journée entre le moment où on ouvre une PR et le moment où elle est mergée. Ça laisse, normalement, tout le temps pour une CI de passer. Pour arriver à ce résultat, plusieurs options sont possibles, comme par exemple fixer un délai maximal avant fusion, pour laisser la chance d'avoir une revue et du test, puis de fusionner dans tous les cas après ce temps, la revue et le test se faisant alors sur le tronc commun. Vous pouvez ajuster cette variable dans le temps en fonction de la fréquence des problèmes liés aux attentes. Par exemple, si l'équipe a des testeurs qui devraient idéalement passer sur la branche pour valider des tests, cette durée pourra être plus grande, tant qu'aucun goulot d'étranglement ne se forme.

Une autre option pourra être de réaliser plusieurs tâches/commit dans la même feature branch pour éviter d'avoir à attendre que le code soit fusionné, si les tâches sont vraiment proches et que l'ensemble ne devient pas "trop gros". Tout est question de dosage, aucune règle ne doit être dogmatique.

Enfin, dans des contextes où une équipe connaît des problèmes récurrents de gestion des conflits liés à de nombreuses branches parallèles, ou lorsque des impératifs de livraison continue ultra-rapide sont essentiels, envisager un modèle "trunk based" peut être pertinent.

Conclusion

J'ai déjà travaillé avec des équipes dont les pratiques rendaient l'utilisation de git flow inconfortables. Logiciels monolithiques ou fortement couplés, besoin de relecture de tout le code par un seul lead developer, absence de CI, etc.
Le résultat était presque toujours des branches qui mettaient un temps infini à être fusionnées, des conflits complexes et une gestion des livraisons très difficile.
Bien que la recherche d'une meilleure stratégie de gestion de branches ait été envisagée, c'est bien les pratiques qui étaient à améliorer.

En ce qui me concerne, je n'ai encore jamais trouvé de raisons suffisantes de sortir du confort dans lequel je suis avec git flow. La gestion fine de l'historique, la possibilité de livrer rapidement des correctifs en production sans avoir besoin de livrer des choses plus compliquée, et la CI/test sur chaque branche pour repérer plus facilement les problèmes sont, en ce qui me concerne, un vrai avantage.

Pour autant, Git Flow n'est ni universel, ni magique, et il est bien entendu important de s'adapter au contexte. Certains modes de distributions de logiciel ou certains fonctionnements d'équipe ne se prêtent pas à l'utilisation de git flow, et il est important de savoir reconnaitre ces situations pour utiliser un flow approprié. Je ferai bientôt un article à ce propos :)

Tumblr
Pinterest
LinkedIn
WhatsApp

<< Bienvenue à Francepunkscene.net >> Faire un site statique avec Django

2022 - tominardi.fr