Choisir la bonne stratégie de branchement pour les versions

11

Commencer avec une nouvelle équipe de développement sur un nouveau projet et nous devons définir notre stratégie de branchement pour notre référentiel source ( par exemple Microsoft Team Foundation Server 2010 ). Nous avons eu une discussion délicate sur l'opportunité ou non de ...

A . Avoir une branche Release à partir de laquelle nous faisons des builds de production, puis étiqueter lorsque quelque chose est réellement publié

OU

B . Avoir une nouvelle branche Release pour chaque nouvelle Release en production ( Ex. Version 1, 2, 3, etc ... )

L'option A semble assez simple, mais nous ne savons pas si cela entraînera des problèmes à long terme. L'option B semble créer simplement de nombreuses branches à vie longue qui ne s'empilent qu'avec le temps.

Quelqu'un a-t-il une expérience qui pourrait nous aider à décider? Plus précisément, je cherche à savoir où se situent les problèmes de douleur pour un seul choix. N'hésitez pas à fournir une expérience spécifique relative aux implications de TFS et / ou de gestion des versions.

JoeGeeky
la source

Réponses:

15

Option A. Il suffit d'utiliser la ligne principale et de baliser pour la publication

Avantages:

  • Vous évitez de fusionner l'enfer.
  • Le maintien de la ligne principale encourage certaines des meilleures pratiques telles que la bonne planification des versions, n'introduisant pas beaucoup de WIP, utilisant la ramification par abstraction pour traiter le travail à long terme hors bande, et utilisant le système ouvert ouvert et les fonctionnalités configurables pour gérer la gestion des travaux en cours qui peut; ou pas; doivent être désactivés maintenant ou à l'avenir afin de libérer ou d'éviter une restauration complète.

Les inconvénients:

  • Le traitement des travaux en cours devient un problème et ajoute à la zone potentielle d'attaque de surface au moment de la libération. Cependant, si vos développeurs sont disciplinés, les nouvelles fonctionnalités doivent être configurables et modulaires et donc facilement désactivées / activées, ou il n'y a pas de WIP et à chaque point de sortie, tout le travail est terminé ou n'a pas encore été démarré (par exemple Scrum).
  • Les changements à grande échelle / hors bande nécessitent plus de réflexion à l'avance pour être mis en œuvre (par exemple, ramification par abstraction).

Personnellement, je préfère cette approche. La couverture du code et les tests unitaires devraient identifier le code qui n'est pas prêt à sortir et les gens ne devraient pas travailler sur du code qui ne sera pas publié pendant l'itération en cours. La ramification par abstraction ou d'autres mécanismes peut être utilisée pour faire face aux changements à long terme et aux travaux en cours.

Lorsque vous ne le faites pas, vous commencez à vous retrouver avec des problèmes de fusion, du code périmé, des fonctionnalités qui ne sont jamais publiées, etc.

Option B. Branche par libération

Avantages:

  • Vous pouvez commencer à travailler sur l'itération suivante pendant que l'itération actuelle termine son cycle de tests d'acceptation.
  • D'autres trucs, je suis sûr.

Les inconvénients:

  • Des tonnes de branches.
  • Vous devez toujours baliser les branches aux points de sortie.
  • Encore faut-il gérer le WIP et fusionner le WIP de la branche de la version précédente dans la branche de la prochaine version s'il ne réussit pas et doit toujours le désactiver ou le retirer de la branche de la version et réexécuter les tests d'acceptation
  • Les correctifs doivent être appliqués à plus de branches (version branche + correctif + nouvelle balise + fusionner le correctif dans la branche vnext et éventuellement vnextnext selon l'endroit où le correctif se situe.)

Je ne suis pas un grand fan de cette solution ^ _ ^.

En général, je recommanderais simplement de rester fidèle à la ligne principale. Si vos développeurs ont du mal à ne pas écrire de WIP qui peut être facilement tiré en cas d'échec de la coupe ou qui est archivé tôt pour la prochaine version, vous pouvez commencer à parler du balisage du code au point où il devrait être terminé et se ramifier. à partir de là si nécessaire pour corriger les défauts et les bogues négligés que vos tests unitaires de développeurs n'ont pas réussi à détecter.

Idéalement, je pense que vous voulez que ce soit le processus d'exception, pas la règle.

Option C. Option Bonus fou

Si vous voulez devenir sophistiqué, vous pouvez également envisager un modèle de branchement par histoire d'utilisateur / par fonctionnalité. ( Une idée terrible dans TFS ou tout autre non DVCS tout en étant incroyablement triviale à mettre en œuvre si vous utilisez un DVCS comme git ou mercurial ).

Dans le passé, j'ai implémenté les éléments ci-dessous pour une équipe de maintenance d'employeurs précédente qui travaillait avec une base de code héritée qui ne pouvait pas être facilement transférée vers mercurial depuis svn. Beaucoup de travail inutile a été impliqué pour répondre à une exigence commerciale d'une ligne principale toujours libérable plutôt que de simplement mieux coordonner les versions, mais. . .

  1. Les fonctionnalités ont été développées par les développeurs dans la branche de développement de leurs équipes.
  2. Lorsqu'une fonctionnalité est prête à être examinée par les pairs, les développeurs la regroupent en une seule fusion de la branche Dev vers la branche CR et incluent l'ID de la fonctionnalité / histoire utilisateur dans le titre. * Appliqué par un crochet de pré-validation *
  3. Après avoir passé CR, un outil d'administration est utilisé pour promouvoir la fonctionnalité dans la branche QA. (J'ai écrit une petite application de terminal qui répertorie les user stories présentes dans les différentes étapes d'acceptation et permet à l'opérateur de la promouvoir ou de la rétrograder entre ces étapes d'acceptation)
  4. L'assurance qualité exécute des tests d'automatisation et d'utilisabilité manuelle. Si la fonctionnalité est bonne, elle est poussée dans la branche de publication (ligne principale). Si la fonctionnalité est rejetée, elle est rétrogradée / retirée de la branche QA jusqu'à ce que les développeurs puissent résoudre les problèmes soulevés pendant le test et ajouter soumettre un patch à la branche CR.
  5. Si le code a été rétabli à partir de la branche QA et qu'un correctif est appliqué, l'outil terminal réappliquera les modifications nécessaires pour ramener la fonctionnalité sur la branche QA à partir de la branche CR afin que QA puisse réexaminer le code et soit le promouvoir soit rétrograder à nouveau.
  6. À tout moment, la branche de publication doit être dans un état libérable stable.
  7. Après une version, les nouveaux Dev, QA et CR sont issus de la ligne principale.
Keith apporte
la source
@Keith_Brings Ceci est un très bon résumé, merci. Comme vous l'avez déjà indiqué, l'option C n'est pas vraiment une option puisque j'utilise TFS, mais intéressante néanmoins.
JoeGeeky
Je ne vois pas comment l'option A peut fonctionner. Dans mon entreprise, nous proposons différentes versions pour différents clients. Si nous continuons à développer des fonctionnalités / correctifs sur la version 1.0 et travaillons également activement sur la version 2.0, et peut-être même 3.0, nous ne pouvons pas faire tout cela sur une seule branche. Peut-être avez-vous le luxe de profiter de l'option A en raison de votre modèle de version. Mais ce n'est pas le modèle de sortie de tout le monde, et pour ceux d'entre nous coincés avec un glissement de fonctionnalités ou plusieurs versions parallèles, nous devons utiliser l'option B.
void.pointer
6

Nous avons des branches distinctes pour chaque version que nous publions (environ 4 par an). Il est très pratique lorsque vous devez extraire une version spécifique.

Si vous avez besoin de conserver quelques versions plus anciennes, je ne pense pas que l'étiquetage ferait l'affaire. Avec des branches de versions spécifiques, vous pouvez appliquer des correctifs à chaque branche séparément (ou à une sélection d'entre elles) sans vous soucier des autres versions.

Cela facilite également la comparaison des versions lorsque vous recherchez le moment où un bogue ou une fonctionnalité a été introduit.

Ne vous inquiétez pas du nombre de succursales ou du temps qu'elles passent sans changements. Votre système de versioning vous donne le contrôle et fournit un historique du développement de votre projet. L'histoire a tendance à ne pas changer ... Et ne vous inquiétez pas si vos CV ne sont pas capables de faire face. Nous utilisons Perforce, plus de 9000 fichiers dans une branche de développement, jusqu'à 50 branches de développement pour les versions sur lesquelles nous travaillons et, comme déjà dit, une seule branche par version que nous publions. Perforce ne respire même pas plus fort.

En bref: simplifiez-vous la vie en tant que développeur / mainteneur / correcteur de bogues / chasseur de problèmes et ne vous inquiétez pas du nombre de branches ou du nombre de fichiers. Tout cv qui se respecte se débrouillera.

Éditer:

Nous ne souffrons pas du tout de confusion quant au nombre de succursales que nous avons. Notre schéma de dénomination pour les branches de publication et notre politique 1 branche 1 branche pour les branches de développement (ou de travail) peuvent avoir quelque chose à voir avec cela.

Les branches de version sont nommées en fonction de la version qu'elles détiennent, c'est-à-dire: R2011SP1 pour la version 2011 Service Pack 1. Nos branches de travail ont des noms moins intelligents: sub01, sub02, sub03 etc. Le "sub" vient du fait que toutes les branches de travail sont des sous-branches de la branche acceptation. La branche d'acceptation étant celle où sont collectés tous les problèmes prêts à être publiés.

Notre politique de branche de travail 1 problème 1, combinée au fait que notre système de suivi des problèmes a été personnalisé avec un champ "branche" garantit que nous savons toujours quel problème est développé dans quelle branche. Lorsqu'un problème est intégré dans la branche d'acceptation, ce champ est mis à jour. Cela signifie que nous savons toujours quels problèmes sont prêts à être publiés (une fois les tests d'acceptation terminés). De même, nous mettons à jour ce champ lorsqu'une branche de publication est créée et de cette façon, nous pouvons toujours retrouver la version publiée.

Marjan Venema
la source
1
Je crois que vous pouvez créer des branches à partir des étiquettes dans TFS. Donc, vous devriez être d'accord pour les correctifs sur les versions actuelles du produit tant que vous n'avez pas oublié l'étiquette.
Keith apporte le
@KeithBrings C'est exact, je viens de tester cela et vous pouvez en effet créer une branche à partir d'une étiquette.
JoeGeeky
@MarjanVenema Je ne suis pas tant préoccupé par la charge du système que par la confusion qu'un grand nombre de branches peut provoquer. Je suis également un peu préoccupé par le fait que les modifications apportées à la pile des branches de versions ne seront pas fusionnées dans d'autres branches de versions qui devraient les obtenir, sans parler de la ligne principale. Avez-vous rencontré ce genre de problèmes?
JoeGeeky
@JoeGeeky: non, pas de confusion du tout. Voir la mise à jour de ma réponse.
Marjan Venema
2

Tout dépend du contexte: à quelle fréquence publiez-vous et ce qui se trouve dans une version.

Voici un peu une étude de cas que j'avais avec mon ancien travail, en utilisant la méthode B (nous l'avons appelé branche par objectif ).

Pour mettre l'histoire en contexte,

  • Une version consistait en de nouvelles fonctionnalités dans notre logiciel: nouveaux modes de jeu, nouvelles fonctionnalités, nouvelles options de configuration.
  • Le cycle de sortie a été assez long: nos clients étaient des universités qui s'en tiendraient à un ensemble de fonctionnalités pendant généralement un an.

Le développement principal a été effectué dans le tronc jusqu'à ce que nous atteignions un état complet pour une certaine version. À ce stade, nous créerions une branche, par exemple projectname-janvier2012 et ferions nos tests de qualité et corrigions les bogues dans cette même branche. Une fois que nous étions prêts pour une version publique, nous étiquetions le code dans cette branche et le publions.

Cependant, le développement de la version ne s'est pas terminé à cette balise. Inévitablement, nous avons eu des clients qui ont trouvé des bogues ou de petits problèmes avec la version. Donc, dans ce cas, tout ce que nous devons faire est de revenir à cette branche, de corriger le code et de créer une nouvelle version balisée de la branche de janvier 2012 à publier, et de fusionner les correctifs dans le tronc.

Dans notre cas, cette approche était favorable car certains utilisateurs préféraient rester avec des versions plus anciennes avec un ensemble limité de fonctionnalités, ou tout simplement parce que le coût du déploiement sur leur infrastructure d'une toute nouvelle version plutôt que d'un correctif a causé certains problèmes.

Les questions que vous devez vous poser sont donc:

  • À quelle fréquence dois-je libérer?
  • Mes versions seront-elles 100% rétrocompatibles?
  • Mes clients seront-ils d'accord avec une mise à niveau complète pour corriger les bugs?

Si vous sortez souvent, cela ne vaut peut-être pas la peine d'avoir des branches pour chacun d'eux. Cependant, si votre cycle de publication est assez long comme mon ancien cas d'utilisation, et que le déploiement, la compatibilité descendante et les clients qui s'accrochent aux anciennes versions peuvent être des risques, l'option B vous épargnera certainement beaucoup de douleur, rendra les choses beaucoup plus faciles à prendre en charge vos clients au moindre coût lié à l'encombrement des succursales.

Bushibytes
la source
J'aime la façon dont vous appelez cette option. Dans ce cas, nous sommes nos propres clients ( en quelque sorte ), donc le déploiement restera largement sous notre contrôle. Nous sommes également une boutique Scrum et nous prévoyons des cycles de sortie assez fréquents ( par exemple toutes les 2 à 4 semaines ). Bien que nous espérons prendre en charge les mises à niveau progressives, la compatibilité descendante ne sera un problème que le temps qu'il faudra pour déployer les mises à niveau, alors ... minutes peut-être. De ce son; dans votre expérience; l'option B n'est peut- être pas le meilleur choix pour moi. Merci pour l'info, très intéressante.
JoeGeeky
Ah oui, dans ce cas, l'option B sonne comme un fouillis avec peu de retour. Je voulais juste souligner que les deux options sont viables et ont chacune leurs avantages. J'ai oublié de mentionner explicitement: comment gérez-vous les corrections de bugs? Sont-ils mis exclusivement dans de nouvelles versions ou sont-ils dans des correctifs / anciennes versions corrigées?
Bushibytes
1

Je préfère l'option A. Développer sur les versions de tronc et de branche lorsqu'elles sont stables. Cela limite considérablement le travail d'intégration des correctifs appliqués à la version de production.

J'ai été engagé pour aider une équipe qui a tenté l'option B à se remettre sur la bonne voie.

Quelques points à considérer.

  • Migrez les correctifs vers l'avant à travers toutes les branches de code actives. Cela peut être fait par fusion, correction et / ou redéveloppement. Ceux-ci doivent être entièrement gérés pour garantir qu'un correctif est appliqué à toutes les versions appropriées, puis au tronc.
  • Considérez les branches de fonctionnalités pour permettre le développement de fonctionnalités indépendamment du flux de code principal. Ceux-ci sont conseillés pour les changements expérimentaux. N'hésitez pas à abandonner les branches de fonctionnalités si la fonctionnalité ne fonctionne pas.
  • Marquez et suivez vos points de fusion.
  • Branchez vos versions si nécessaire. Je trouve que c'est normalement lorsque la version est prête pour les versions candidates. Dans certains cas, l'introduction de changements incompatibles dans le tronc peut forcer et ramifier tôt. Considérez une branche de fonctionnalité.
BillThor
la source
0

Je travaille depuis un certain nombre d'années sur un système qui utilise quelque chose entre les deux schémas que vous décrivez. L'essentiel est qu'il existe un schéma de numérotation à plusieurs niveaux. Le niveau externe est essentiellement la version de l'API, et il est géré sur des branches (avec des fusions croisées appropriées lorsque quelque chose doit être corrigé sur plusieurs branches) et le niveau interne correspond aux versions exactes effectuées, qui sont gérées avec des balises.

En particulier, si nous savons de quelle version exacte un client dispose, nous savons exactement à partir de quelle source le code a été créé et pouvons en faire un double exact afin que nous puissions voir exactement ce qui se passe. C'est très important pour le support! Pourtant, le niveau externe des branches, les versions d'API que nous publions actuellement, elles évoluent avec le temps (le principal tronc de développement obtenant la majorité des nouvelles fonctionnalités). De plus, lorsque nous faisons une nouvelle version majeure de l'API, nous faisons une nouvelle branche pour prendre en charge cela à partir de (afin que le tronc puisse toujours être orienté vers le développement dur) et nous nous demandons si nous devons mettre fin à la vie de la prise en charge la plus ancienne actuelle. branche.

Ainsi, je recommande quelque chose qui est vraiment un mélange de A et B ; les deux ont de bons aspects, mais aucun n'est complet en soi. Utilisez le meilleur des deux mondes.

Associés Donal
la source
0

J'ai utilisé TFS pour implémenter efficacement l'option (B) dans le passé.

La ramification / fusion est un excellent outil lorsqu'elle est effectuée en petits morceaux. La difficulté ne réside pas dans la création d'une branche (c'est stupide facile), ni dans une semaine de travail pour sauvegarder l'arborescence (c'est généralement aussi facile) ... c'est dans le fait que le système CI derrière votre contrôle de source fonctionne automatiquement pour tu.

Parce que la branche est inutile si le système ne crée pas et n'exécute pas automatiquement des tests pour votre branche.

Nous avions personnalisé le workflow de construction par défaut de TFS pour reconnaître les chemins relatifs des changesets, et établi une convention par laquelle la personnalisation pouvait reconnaître une nouvelle branche (par opposition à simplement un nouveau sous-dossier sous une racine de développement). C'était lisse, facile à brancher, facile à tuer une branche, et nous avons reçu un retour continu de notre système pour les compilations et les tests.

Je vois beaucoup de gens déclarer à quel point ces stratégies sont impossibles sous TFS, et je pense que cela est dû à un manque de familiarité avec les possibilités d'un moteur de construction basé sur XAML. TFS n'est pas seulement le contrôle de code source, c'est une solution complète et doit être utilisé comme tel.

Craig Brunetti
la source