Qui a besoin de plusieurs branches ?
02 avr. 2013 Olivier Azeau En français 13
Si les logiciels de gestion de versions sont globalement rentrés dans les habitudes de tout développement logiciel, la façon de les utiliser est loin d'être uniforme. Cette multiplicité des usages est particulièrement flagrante lorsque l'on évoque la notion de branches.
Wikipedia définit ainsi les branches :
Lorsque des modifications divergentes interviennent hors conflit, il se crée des branches. Le fait de vouloir rassembler deux branches est une fusion de branches.
Les branches sont utilisées pour permettre :
la maintenance d'anciennes versions du logiciel (sur les branches) tout en continuant le développement des futures versions (sur le tronc) ;
le développement parallèle de plusieurs fonctionnalités volumineuses sans bloquer le travail quotidien sur les autres fonctionnalités.
La nécessité de maintenir plusieurs versions livrées d'un même logiciel est un besoin courant dès que le logiciel est proposé à plusieurs clients .Un nouveau client est intéressé par la version 4.0 qui vient tout juste de sortir tandis qu'un autre préfére rester sur la 3.2 sur laquelle il a investi beaucoup de temps de formation. Le coût de passage à une nouvelle version majeure serait prohibitif. Du coup l'équipe de développement doit simultanément développer la version courante et corriger les bugs trouvés sur des versions plus anciennes. Pour cela, on travaille avec des release branches.
Par ailleurs, le développement d'une fonctionnalité ne peut pas se faire du jour au lendemain. Cette même équipe, en développant la version 4.0 ne peut pas se contenter de ne travailler que sur les fonctionnalités en cours. Il y a cette future fonctionnalité que tant de clients attendent et qui sortira, si tout va bien, dans la version 4.5 ou, au pire, l'an prochain dans la 5.0. Et cette fonctionnalité prend tant de temps à mettre en oeuvre qu'il faut d'ores et déjà y travailler. Mais on ne peut pas le faire sur la version courante. On va donc utiliser une feature branch qui sera fusionnée ultérieurement avec la version courante du moment.
Ce scénario vous semble familier ?
C'est le lot de nombreuses équipes pour qui le contrôle de version est devenu une des pierres angulaires de leur processus de développement et occupe une part non négligeable de leur activité quotidienne.
L'émergence récente des outils de gestion de version décentralisés facilite grandement la tache de ces équipes.
Historiquement, avec les gestions de version centralisées, les branches sont coûteuses. Il faut demander au serveur de créer une branche, ce qu'il va faire en copiant plus ou moins intelligemment les données d'un dossier dans un autre dossier. En fait, dans une approche centralisée, diverger du point central est l'exception. On s'attend à ce que le développeur rapporte au plus vite sa production sur le serveur et résolve les conflits pouvant intervenir avec la production des autres développeurs.
Dans les outils décentralisés, les branches sont en quelque sorte incluse dans l'ADN. On s'attend à ce que des divergences surviennent et on met en place les fonctionnalités permettant de les fusionner au mieux, de piocher ici ou là des modifications individuelles. Bref on n'a pas peur des branches et on les accueille avec plaisir.
Sauf que...
Comme disait l'autre, si ton seul outil est un marteau, tu verras tous les problèmes comme des clous...
J'ai la désagréable impression que, les outils décentralisés ayant le vent en poupe dans les usages, l'utilisation à outrance de branches pour la moindre raison devrait aller de soi parce que c'est devenu si simple de les manipuler...
On ne peut nier que Linus Torvalds ait besoin de git parce qu'il travaille indirectement avec des centaines de personnes et qu'il ne fait confiance[1] à quasiment aucune d'entre elles. Le modèle décentralisé semble la réponse idéale aux gros projets open source.
Mais qui a des besoins vaguement similaires ?
Les branches de release ne sont pas une fatalité. Un logiciel peut évoluer jour après jour sans véritable version majeure en étant continuellement livré à ses clients. Si certaines fonctionnalité sont trop dures à avaler immédiatement, on imagine des mécanismes de configuration pour les activer à la demande.
Les branches de feature ne sont pas une fatalité. Les mêmes mécanismes de configuration sont utilisés pour des fonctionnalité non terminées : leur code sera présent mais non exécuté dans la version livrée.
Pourquoi mettre tout son effort dans l'utilisation avancée d'un gestionnaire de version pour atteindre ces objectifs alors que la richesse des langages de développement nous permet de réaliser la même chose à l'intérieur du logiciel lui-même ?
On parle de plus en plus de feature flipping, c'est à dire de la capacité à activer/désactiver une fonctionnalité via une option configuration. Mais je crois que ceci n'est que la partie émergée de l'iceberg à venir.
Un flag de configuration qui induit une armée de "ifs" à divers endroits du code en fonction de la fonctionnalité à maintenir n'ira jamais bien loin. Diverses techniques permettent toutefois de se débarrasser des ifs. On peut arriver à un feature swapping extrêmement maintenable avec du polymorphisme et de l'injection de dépendance.
Bref, une meilleure approche de la conception logicielle rend le concept de branche obsolète.
Mais on peut aller plus loin.
Dans une équipe agile, donc à taille humaine, où la confiance est inconditionnelle et où l'on pratique sans relâche la propriété collective de code, les feature branches n'ont pas d'intérêt car tout le monde travaille sur la même chose avec des échéances très courtes. Tous les membres de l'équipe modifient donc plusieurs fois par jour la version de référence du logiciel.
Si on englobe dans le concept les utilisateurs du logiciel, on laisse également tomber les release branches car l'utilisateur n'a que faire de l'existence de plusieurs versions. Il veut seulement utiliser facilement et rapidement la meilleure version qui soit.
Dans mon équipe, la branche unique est devenue un facteur important qui nous permet de développer sereinement et continuellement notre logiciel depuis plusieurs années. C'est ce dont je parlerai, entre autres choses, dans la session "Deux ans dans le flux" lors du prochain Mix-IT.
Notes
[1] peut être à juste titre mais c'est une autre histoire
Salut Olivier,
Merci pour cet article auquel j'adhère (presque) totalement. C'est très (trop) courant de voir un fort engouement, pour un outil et ses fonctionnalités, entraîner les utilisateurs de cet outil dans des dérives les éloignant des fondamentaux de l'outil lui-même (les branches en l'occurence).
Pour les "features branches" sur le tronc commun ne devrait pas être utilisées, surtout en mode agile, où une fonctionnalité devrait pouvoir être intégrée petit à petit. Par contre, j'y suis favorable sur le poste de chaque développeur. Moi même, j'utilise Git sans modération pour les travaux en court, mais à court terme, ou les essais en tous genres.
Et concernant les "releases branches", elles ne sont plus nécessaires pour le développement d'application SaaS qui pourrait représenter une grosse partie des futures développements.
Xavier
Bonjour Olivier,
merci pour cet article très interessant.
Le sujet des branches est effectivement souvent central en développement logiciel et assez peu souvent traité.
Comme écrit par Xavier, il faut distinguer les éditeurs en mode SAAS et les traditionnels. Pour le SAAS, la question ne se pose pas...
Mon experience (en mode traditionnel) est que ce sont les clients qui exigent souvent une gestion de branche, par frilosité de récupérer une nouvelle version qui contiennent trop de changements et qui leur prennent trop de temps à intégrer. A leur décharge, on peut aussi considerer le fait qu'ils ont pu souffrir de nouvelles versions qui contenaient des regressions ou des changements d'API non/mal communiqués et qu'ils sont refroidis.
Laurent
@Xavier,
Concernant le poste du développeur, si on voulait être précis dans les termes, il faudrait plutôt parler de "divergence de code" dont la réalité technique dépend de l'outil considéré (branche, working copy...)
Parler de "branche unique" est en fait un raccourci simplificateur pour dire que les divergences de code doivent être réduites au strict minimum dans l'espace et dans le temps.
En fait, je n'ai pas d'avis sur le mode de travail de chaque développeur du moment du moment que son code de production ne reste pas plus de 2 ou 3 heures hors de la version de référence de l'équipe...
En ce qui me concerne, peut importe si je fais un spike sur une copie ou sur une branche locale. Tout dépend de l'outil à disposition. Par contre, je ne mélange jamais essais et code de prod. Je considère par exemple que le cherry picking depuis un spike vers le "vrai" code est une pratique risquée car elle ne garantit pas le transfert du pilotage par les tests.
@Laurent,
Evoluant moi même dans un environnement proche du SaaS par le modèle économique mais encore largement "traditionnel" par les aspects techniques (induits notamment par la forte présence de matériel spécifique), je peux comprendre le problème des versions. Mais je crois que celui-ci n'est pas une fatalité. Même s'il y a des morceaux de systèmes versionnés pour diverses raisons, on peut rendre d'autres parties "version-less". C'est en tout cas ce que nous faisons dans notre équipe en communiquant avec les parties versionnées à travers des plug-ins dédiés (un par version) et qui implémentent tous le même contrat vis à vis du reste de notre logiciel.
Bonjour,
Même si tu m'a déjà expliquer rapidement ta solution par twitter, je serai assez intéressé par voir un article plus approfondi sur ta solution pour utiliser une branche unique et ce que tu met en oeuvre au niveau de l'architecture du logiciel en lui même.
En tout cas, ton blog est très intéressant et donne à réfléchir au sujet de certaines habitudes qu'on nous a inculqués et qu'on applique peut être de façon un peu trop systématique.
@Armaklan
Merci !
Question articles sur l'aspect architecture, il y en a quelques uns de très théoriques sur ce que l'on met en oeuvre chez nous. Il y a aussi des exemples de mise en pratique (on voit par exemple transparaître l'émergence des abstractions dans mon dernier billet, il y aussi celui-là sur l'injection de dépendances) mais c'est difficile de faire un billet concret sur le sujet sans embarquer le poids du contexte...
Les idées générales, on les retrouve aussi sur la toile dans tout ce qui parle de "branch by abstraction" et sinon il y a une video d'Uncle Bob incontournable sur le sujet.
Bonjour,
Je partage ta vision sur le fait qu'il vaut mieux maintenir une seule "version stable" de l'application.
Parcontre pour moi le système de branche est vraiment l'outil qui facilite le travaille et je ne me verrais pas vivre sans, j'en crée tous les jours.
Exemple:
J'ai un logiciel SAAS, avec un système de facturation qui ne permettait de "livré" une commande seulement si la société avait reçu le règlement (on ne visait que les particuliers/TPE)
Sauf qu'on a décroché un contrat avec une grosse société qui elle voulait un paiement à 30jrs.
Donc modification du système de facturation (~1semaine)
Sauf que pendant cette semaine, j'ai constaté pendant mon developpement l'existance d'un bug rare, mais grâve.
Donc j'ai fais une branche de ma version "production", fait ma correction de bug, mis en prod, et fusionné cette correction avec ma branche "nouveau systeme de facturation"
Sans branche, comment vous auriez fait ?
Vous auriez repoussé la livraison du patch à la fin de la nouvelle fonctionnalité ?
Autre situation, une partie de mon équipe travaille sur la fonctionnalité A, et une autre sur la fonctionnalité B.
l'équipe A est en a ses tests, alors que l'équipe B est en plein développement(et donc l'appli n'est plus fonctionnelle)
Si tous les dev' utilise la même branche, comment l'équipe A peut elle livrer sa fonctionnalité, si lors de ses tests l'appli n'est plus fonctionnelle ?
Alors qu'une branche "feature-A" pour l'équipe A et une branche "feature-B" pour l'équipe B
Tout le monde peut "commit", partagé sans impacter le travail des autres.
@Fluf,
Le cas de correction de bug en prod pendant le dev d'une nouvelle fonctionnalité rejoint les notions de feature swapping / branch by abstraction mentionnés dans le billet et le commentaire.
Concrètement, les changements induits par la nouvelle fonctionnalité doivent être conceptualisés. Si on prend l'exemple de la modification du système de facturation, on pourrait avoir (je schématise car je ne connais pas du tout l'application en question) une abstraction "facturation" qui a deux implémentations "facturation avant livraison" et "facturation à 30 jours". Au départ, l'abstraction "facturation" n'existe pas car on n'a jamais pensé à avoir autre chose qu'une facturation avant livraison. Donc on commence par faire émerger l'abstraction à iso-fonctionnalité, c'est à dire avec "facturation avant livraison" comme seule implémentation. Puis, on peut commencer à introduire une deuxième implémentation "facturation à 30 jours". Pendant tout ce temps, l'application reste entièrement fonctionnelle. Tant que l'implémentation de "facturation à 30 jours" n'est pas finalisée, elle est tout simplement désactivée et invisible pour les utilisateurs.
Cette approche permet de conserver une application dont la branche de développement est systèmatiquement utilisable pour la prod. Du coup, si un bug fix urgent survient, on le fait sur cette branche unique que l'on met en prod aussitôt.
Pour l'exemple des deux équipes qui travaillent en parallèle sur des features différentes, c'est la même chose. Il faut que le développement se fasse en permanence avec une application qui reste toujours fonctionnelle malgré les commits des uns et des autres. Pour cela, il faut encore et toujours faire émerger les abstraction adéquates lorsque le besoin se présente.
Non seulement ça évite la gestion des branches mais en plus ça permet de tendre vers une architecture beaucoup plus maintenable car de plus en plus capable d'absorber en douceur les évolutions à venir.
Ok, je n'avais pas vu ca comme ca.
Mais justement ce type développement, ne rajoute t'il pas de la complexité pour au final pas grand chose ?
Le temps de penser l'abstraction, de l'implémenter de modifier un code(qui va etre abandonné) pour qu'il colle au nouveau systeme, et seulement une fois ca fait, on commence la nouvelle fonctionnalité.
On passe beaucoup d'energie sur une modification de code non nécessaire.
Alors qu'avec un VCS qui gère les branches on aurait sauté les 1eres étapes et ne se concentrer que sur celle qui est obligatoire: "développement de la nouvelle fonctionnalité".
Je suis d'accord sur le fait qu'il faut essayer de rajouter de l'abstraction, mais si c'est pour au final avoir 50 concepts qui cohabitent alors qu'à un instant T dans la vie du logiciel une seule n'est utile.
J'ai du mal à imaginer des cas ou un VCS ne facilite pas énormément le travail.
Bonjour,
Je suis complètement en accord avec les réponses de Fluf. Vous soulevez des points intéressants dans votre billet, mais au final vous faites une croix sur certaines fonctionnalités d'un VCS. Le concept de branches a été créé pour cette utilisation, quelle est la raison de ne pas l'utiliser ? Vous ne répondez pas réellement à cette question dans votre exposé.
De plus, l'utilisation de branches "Features" et "Hotfix" permet d'avoir une plateforme de "Prod" et/ou de "Dev" saine. J'expose un exemple :
Imaginez le développement d'une nouvelle fonctionnalité. A 30% de vos développements, on vous fait part de l'annulation de celle-ci et de travailler sur autre chose. Si vous utilisez le "Feature flipping", il y aura un bout de fonctionnalité dans votre plateforme qui sera inutilisable. Vous allez peut être me répondre de faire un "Revert" et d'oublier cette fonctionnalité, mais si vous aviez une branche elle pourrait être conservée ; On ne sait jamais, le client va peut être demander la finalisation de cette tâche.
Personnellement j'utilise ce type de "Workflow" en local : http://paikialog.wordpress.com/2010... et je "Push" exclusivement mes branches "Master" et "Develop" sur le serveur de Git. Quelle est votre "Workflow" généralement ?
@Fluf,
Moi je constate que, au contraire, rajouter des abstractions me permet de simplifier mon code, de supprimer des duplications et de rendre le code plus modulaire donc plus facilement testable.
En fait, toutes les abstractions sont utiles tout au long de la vie du logiciel car elles ont toujours au moins 2 implémentations : celle qui est utilisée en prod et celle qui est utilisée en test pour vérifier le comportement des composants utilisateurs de l'abstraction en question.
Je renvoie à ma série de billets sur la conception logicielle pour plus de détails.
@Yohann,
Peut être que toutes les réponses ne sont pas dans le présent billet mais celui-ci s'inscrit inévitablement dans le cadre de tout ce qui a pu être écrit jusqu'ici sur ce blog !
En fait, je ne me suis jamais levé un matin en me disant "je vais arrêter d'utiliser des branches". C'est plus une conséquence qu'autre chose.
Pour lister ce qui m'a amené à cette conséquence, il faut probablement chercher
Pour répondre plus précisément sur la "fonctionnalité annulée", il faut bien comprendre que les fonctionnalités restent présentes dans notre logiciel. Il se trouve qu'elles ne sont tout simplement pas activées.
Que faut-il préférer ? Une fonctionnalité non utilisée qui reste quelque part dans une branche qui diverge du reste ? Ou un fonctionnalité non utilisée qui reste dans la branche principale et dont le lien avec l'abstraction garantit l'abscence de divergence ?
Pour le workflow, c'est simple : on garde les pastilles jaunes et on vire le reste.
Bonjour,
Je suis très intéressé par le concept de polymorphisme pour gérer le développement de nouvelles versions de fonctionnalités et leur activation progressive.
Peux-tu développer un peu plus?
A+
@gedefah,
Il me faudrait faire un nouveau billet avec un exemple concret.
Ca viendra, mais pas dans l'immédiat...
Je partage complètement cette vision avec une version continuellement fonctionnelle.
Toutefois, je pense que les branches restent intéressantes pour embarquer des patchs sur une version en production.
La principale raison que je vois, c'est que l'application en cours de développement, même si les fonctionnalités sont désactivées, contient des parties "actives" modifiées. Je pense en particulier au code qui va permettre de gérer les abstractions pour les fonctionnalités en cours de développement. Ce code est peut être simple et présente peu de risque de régression mais cela me gêne de relivrer en production du code modifié (même minime) et qui n'a rien à voir avec le correctif en cours. Les vérifications nécessaires ne seront pas de même niveau. A moins de se reposer entièrement sur une validation automatisée (ce qui est rare) cela me semble présenter un risque supplémentaire inutile.