Git ou SVN : Comment Nuance Healthcare a opté pour un modèle de branching Git ?

Matt Shelton
Matt Shelton
Retour à la liste

Cet article est un billet d'invité de Matt Shelton de Nuance Healthcare. Il s'agit du premier d'une série consacrée à la migration de son équipe de Subversion vers Git, aux raisons qui ont motivé cette migration et aux problèmes rencontrés. Matt a également évoqué ce sujet à l'Atlassian Summit 2015. Cette série développera tous les points qu'il n'a pas pu aborder lors de sa présentation de 30 minutes, avec davantage de contexte.

Contexte

Mon équipe travaille au sein du service santé de Nuance. Nous sommes répartis entre plusieurs bureaux et résidences de la côte est des États-Unis et un bureau à Pune. Nous développons des services Web Java pour proposer à NLP[1] des solutions adaptées au marché de la santé.

Pour la plupart, nos clients sont des éditeurs de logiciels dans le domaine de la santé (y compris nous-mêmes) comme des fournisseurs de dossiers médicaux électroniques et des sociétés spécialisées dans l'analytique médicale. Nous commercialisons également certains produits auprès des hôpitaux et des utilisateurs finaux des applications, qui vont des médecins au personnel médical chargé de la facturation. Les gens « normaux » comme vous et moi ne touchons jamais le logiciel créé par mon équipe.

Notre équipe travaille depuis un certain temps à des combinaisons de produits de gestion du cycle de vie des applications. Nous avons commencé par un mélange de Rally Enterprise et Seapine TestTrack Pro, nous avons beaucoup travaillé pendant 14 mois avec Rational Team Concert, puis nous avons finalement migré complètement vers la suite Atlassian (Jira, Confluence, Bamboo, Crucible, Bitbucket et Hipchat). Par le passé, nous avons utilisé Subversion 1.4/1.5 et notre SCM avec une structure de trunk/branches/tags pratiquement normale. Nous utilisions Maven depuis toujours pour gérer nos projets de build et nos dépendances, et nous sommes passés de Jenkins à Bamboo pour l'intégration continue (CI) il y a quelque temps afin de profiter des intégrations plus étroites avec Jira ainsi que de ses fonctionnalités d'agent de build et de déploiement flexibles. Tous les outils que nous utilisons (à présent) sont derrière le pare-feu pour de bonnes raisons[2].

Git ou SVN ?

Nous prenons en charge une dizaine de produits individuels dans nos familles de produits, et les responsables de ces produits se battent toujours pour des questions de hiérarchisation et de calendrier. Il est agréable de voir que notre travail est très demandé. Cela ne veut pas dire que nous nous plaignons. Simplement, cela implique de produire des livraisons à un rythme étrange et de changer de cap au milieu d'un sprint[3].

Par moments, notre processus de développement nous semblait prohibitif. Mon équipe avait souvent la discussion suivante :

Moi : Nous devons maintenant envoyer la livraison 1.8.0 en QA pour des tests de régression afin que le « foo » client passe en bêta la semaine prochaine. Développeur : Je travaille toujours sur ABC-123, qui est en trunk. Ce n'est pas encore terminé. Moi : Le « foo » n'a pas besoin d'ABC-123. Nous pourrons l'intégrer à la prochaine livraison. Développeur : Mais j'y travaille depuis des semaines. Je ne sais pas à quel niveau créer une branche pour produire une livraison. Moi : Eh bien, tu devras extraire manuellement tous tes changements. Tu as environ deux heures, sinon le QA ne sera pas terminé à temps.

Je sais, j'ai l'air insensible ! Ça n'a jamais été mon intention et, bien sûr, j'exagère un peu pour faire passer mon point, mais nous devions vraiment trouver un moyen d'extraire le code hors de cet emplacement de manière temporaire afin de produire une livraison, puis de l'y remettre pour la prochaine[4]. Ce genre de situation arrive en permanence.

Maintenant, je sais que certains d'entre vous pensent : « Subversion prend en charge les branches, Matt… ». C'est en effet le cas, et nous l'utilisons avec SVN 1.4 et 1.5. La création de branches est une opération facile dans SVN ; le merge peut poser des problèmes. Avec les évolutions de SVN, cela s'est amélioré, c'est certain. Mais nous savions qu'il existait de meilleures solutions pour nous. Donc, quand s'est posé la question de savoir si nous poursuivions avec SCN ou si nous passions à Git, nous avons opté pour Git.

Remarque : nous avons brièvement examiné la dernière version de SVN (1.8 à l'époque) pour vérifier si elle permettait de résoudre nos problèmes, mais nous n'avons pas été complètement satisfaits. L'un de nos groupes de pairs possède une installation Perforce importante, ce qui répondait bien à la plupart de nos besoins, mais je ne pouvais tout simplement pas avaliser les coûts de licence. Nous nous sommes également intéressés à Mercurial pendant un temps, mais au final, l'expérience de l'équipe existante avec Git a suffi pour nous convaincre que c'était le bon choix à faire.

Je ne vais pas prendre des gants pour le dire : les outils Atlassian sont une réelle aide pour les équipes qui utilisent Git. D'autres SCM fonctionnent très bien ; notre intégration SVN était suffisante, puisqu'elle nous liait à l'emplacement où des changements étaient apportés à une user story donnée. Les fonctionnalités d'intégration pour les équipes qui utilisent Bitbucket Server[5] sont, elles, plus solides et plus naturelles dans l'expérience d'interface et de développement Jira Software (idem avec Bamboo).

Sachant cela et ayant assisté à quelques démonstrations captivantes lors de l'Atlassian Summit 2013, j'ai fortement encouragé l'équipe à l'essayer. Personne ne s'y est opposé, et nous disposions déjà des licences pour procéder à ce changement.

Sélection d'un modèle de branching Git

Après avoir décidé d'effectuer ce changement, notre premier défi consistait à déterminer le modèle de création de branche Git à implémenter pour notre équipe. Le microsite Git d'Atlassian et cette présentation exceptionnelle de l'Atlassian Summit 2013 expliquent plus en détail ce qu'est un modèle de création de branches. Pour faire court, elle décrit l'utilisation des branches dans Git pour optimiser votre workflow de développement.

Dans SVN, nous avions un modèle de création de branches que je résumerais ainsi : « Créer une branche au moment où nous réalisons que nous... zut ! en avions vraiment besoin »:

  • Le code le plus récent se trouve dans trunk. Les livraisons issues de trunk seront numérotées comme suit : A.B.0-{build}.
  • Si vous devez réparer une livraison basée sur trunk (p. ex., vous constatez un bug dans la livraison 1.2.0-64), une branche est créée. À partir de ce point, des livraisons A.B.C-{build} seront publiées. C est incrémenté dès qu'une livraison est effectuée. Ces branches ne correspondront jamais à un A.B donné, et il peut en exister plus d'une.
  • Nous taguons également chaque livraison dans un répertoire dédié.

Parenthèse sur les versions Il y a de nombreuses années, alors que je faisais mes premières armes en tant que responsable d'une équipe de développeurs, notre pilote de mise en production utilisait un système de contrôle de version que je qualifierais de… comment dire ?… tout sauf intuitif. Chaque livraison consistait en un patch généré à partir la livraison précédente (A.B.n), quel que soit l'emplacement d'origine du patch. Pour connaître l'emplacement d'un élément, mais aussi pour la plupart des commandes de livraison, il fallait consulter svn log. Nous avions affiché l'arborescence sur le mur et nous l'utilisions comme référence. De plus, nos numéros de livraison publics étaient généralement 3.0, 3.1, 3.5, 4.0 ou en tout cas un numéro auquel un client pouvait s'attendre. Cependant, il faut savoir que mon équipe conçoit des services Web et pas des produits en coffret. Chacune de nos API est développée sur la base d'un contrat. Il y a quelques années, j'ai fait en sorte que nos livraisons et, par conséquent, nos builds adhèrent aux principes du contrôle de version sémantique. J'ai souvent dû batailler avec mes supérieurs, mais maintenant, tout le monde a compris pourquoi nous avons adopté ces règles et pourquoi nous n'avons jamais fait marche arrière. Nos partenaires apprécient une telle clarté.

Auparavant, j'ai parlé d'un problème que nous avons rencontré alors que nous travaillions sur une livraison (appelons-la 1.2.0). Une fonctionnalité était toujours en cours de développement alors que la date de livraison se rapprochait. Nous devions faire un pull du code, produire notre livraison, créer une branche vers branches/1.2.1, puis faire de nouveau un merge de ce code, en espérant que personne n'ait un crash de disque dur pendant ce temps-là [6].

Supprimer toute une fonctionnalité issue d'un trunk partagé, ce n'est pas de la tarte. Les développeurs chargés d'accomplir cette tâche en voulaient toujours à la terre entière…! svn blame peut s'avérer utile, comme tout outil de comparaison efficace, mais ça reste une plaie de l'utiliser. J'avais souvent l'impression que c'était de ma faute, que cette perte de contrôle avant l'échéance était due à une mauvaise organisation de ma part [7]. Mon équipe en a souvent fait les frais.

Parfois, nous avons surcorrigé le code pour éviter de nous embêter et nous avons demandé aux développeurs de rester les bras croisés pendant quelques jours (une sorte de gel de code virtuel si vous voulez), le but étant de ne pas polluer le trunk avant une livraison.

Nous savions donc que nous avions besoin, au minimum, de branches de fonctionnalité. Un modèle de branching Git simple s'applique : une branche master pour ce qui est en production et des branches de fonctionnalité pour les fonctionnalités, les bugs, etc. Les équipes doivent gérer le merge afin de s'assurer que ce qui sort de la branche master est bien ce qui est censé se trouver dans la livraison. Auparavant, nous avions un résultat similaire avec une isolation de fonctionnalité relativement meilleure, mais nous voulions être libres de faire ce que nous voulions.

Dans notre environnement, nous devons souvent garder quelques versions en production et nous pouvons avoir besoin de corriger des défauts dans une livraison qui remonte à deux ou trois révisions mineures avant celle sur laquelle nous travaillons en ce moment. Par conséquent, outre de branches de fonctionnalité, nous avions également besoin de certaines branches de livraison (ou similaires) qui nous permettraient de corriger les bugs des livraisons précédentes. L'équipe Bitbucket Server d'Atlassian a résolu le problème. Elle effectue des corrections dans les branches de support longues, puis les merge dans les flux de branches pour que la correction soit communiquée à tous les flux de support.

Son modèle semblait vraiment bon, c'est pourquoi nous l'avons testé avec des prototypes pour voir s'il était adapté à nos besoins. Pour eux, l'« application géniale » est celle qui fait un merge d'une correction en continu dans leur branche develop. Ce concept était sympa. Mais, à chaque fois que nous l'avons essayé, nous avons rencontré un problème avec nos dépendances Maven. De plus, nous ne pouvions généralement pas être sûrs que nous souhaiterions un merge direct du travail d'une version à une autre. Dans certains cas, nous avions besoin d'implémenter la même correction de manière légèrement différente entre les versions. Un merge direct n'était donc pas possible.

Certains membres de l'équipe étaient très favorables à une variation de ce modèle appelé « git-flow ». git-flow est un ensemble de conventions de dénomination et de directives de merge développé par Vincent Driessen. Ce modèle semblait très naturel pour l'équipe. Et, nous aimions sa structure, puisqu'elle éliminait de nombreuses questions de type : « Que faire quand nous avons besoin de x ? ». Les réponses étaient généralement évidentes. Au lieu de vous expliquer ce qu'est git-flow, je vous conseille de vous reporter au tutoriel Atlassian qui lui est consacré.

La seule question qui subsistait avec git-flow était de savoir quoi faire avec ces livraisons longues en production. Puisque la branche master ne cesse d'évoluer, nous ne pouvions utiliser le workflow de hotfix git-flow pour corriger un bug dans une livraison antérieure. D'un autre côté, nous n'avons pas toujours voulu une branche support.

Généralement, une branche hotfix corrigeant uniquement la dernière livraison en production doit suffire, la branche support n'étant là que lorsque nous avons besoin de revenir plus en arrière ou lorsque nous devons maintenir la compatibilité pour une raison quelconque. Nous avons étudié ce cas plus en détail et nous avons déterminé des critères pour l'utilisation d'une branche support plutôt que d'une branche hotfix et d'une mise à niveau mineure de la livraison :

  1. Ce code ne peut pas être trivialement réincorporé dans le développement.
  2. Le partenaire/client ne peut gérer un changement d'interface survenant lors de la dernière livraison.
  3. Une dépendance interne ne peut être modifiée.[8]

Les deux packages d'extension git-flow[9] prennent en charge le concept de branche support, ce qui n'était pas prévu à l'origine dans git-flow, mais qui est devenu suffisamment populaire pour garantir son intégration.

Nous avons aimé le workflow que git-flow nous apportait avec la prise en charge des outils dont nous avions besoin. Dans le prochain billet, je reviendrai plus en détail sur notre expérience lorsque nous avons réellement essayé de le mettre en œuvre dans un projet POC utilisé pour illustrer notre processus de développement. Ce fut une expérience pour le moins instructive !

[1] : Traitement automatique du langage naturel. NOUS POUVONS LIRE VOS PENSÉES. (Non, pas vraiment.)

[2] : Les offres cloud d'Atlassian sont très attractives, mais nous devions absolument garder le contrôle de nos serveurs et de nos données. Bien que nous n'avions pas vraiment besoin de traiter des données PHI, notre logiciel le faisait, et il était important de préserver une sécurité maximale.

[3] : Chut… ne dites rien à Ken Schwaber.

[4] : Qui pourrait de toute façon arriver dans quelques jours seulement.

[5] : Anciennement, Stash. Saluons le rebranding d'automne d'Atlassian !

[6] : Je savais que nous pouvions toujours l'extraire du précédent commit. Je plaisantais.

[7] : Ce n'était pas souvent le cas : généralement, le calendrier d'une personne avait changé et nous devions réagir rapidement.

[8] : Cela fait partie des choses que je ne peux pas développer sur mon blog. Croyez-moi. J'ai de bonnes raisons.

[9] : Le package d'origine de Vincent Driessen n'est plus mis à jour. Cependant, un nouveau fork est régulièrement mis à jour.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant