Monorepositoires: veuillez

image


Une traduction de l'article a été préparée pour les étudiants du cours DevOps Practices and Tools du projet éducatif OTUS .




Vous devez choisir un mono-référentiel, car le comportement qu'il favorise dans vos équipes est la transparence et la responsabilité collective, notamment avec la croissance des équipes. Dans tous les cas, vous devez investir dans des outils, mais c'est toujours mieux lorsque le comportement par défaut est le comportement que vous souhaitez voir dans vos commandes.


Pourquoi parlons-nous de cela?


Matt Klein a écrit un article, "Monorepos: s'il vous plait, ne le faites pas!" (commentaire du traducteur: traduction sur un hub "Monorepositories: please do not" ). J'aime Matt, je pense qu'il est très intelligent, et vous devriez lire son point de vue. Il a initialement tweeté le sondage:


image


Traduction:
En ce jour de nouvel an, je parie à quel point les monorepositoires sont ridicules. 2019 est passée inaperçue. Dans cet esprit, je vous propose une enquête. Qui sont les grands fanatiques? Supporters:
- Monorepositaire
- Rouille
- Mauvaise enquête / à la fois ceux et ceux


Ma réponse a été: "Je suis littéralement ces deux personnes." Au lieu de parler de quel type de médicament Rust, essayons de comprendre pourquoi je pense qu'il a tort au sujet des mono-référentiels. Un peu de toi. Je suis le CTO de Chef Software. Nous avons environ 100 ingénieurs, une base de code d'environ 11-12 ans et 4 produits principaux. Une partie de ce code est dans le polyrepository (ma position de départ), une partie dans le monorepository (ma position actuelle).


Avant de commencer: chaque argument que j'apporterai ici sera appliqué aux deux types de référentiels. À mon avis, il n'y a aucune raison technique pour laquelle vous devriez choisir l'un ou l'autre type de référentiel. Vous pouvez faire fonctionner n'importe quelle approche. Je suis content d'en parler, mais je ne m'intéresse pas aux raisons techniques artificielles pour lesquelles l'une est supérieure à l'autre.


Je suis d'accord avec la première partie du point de vue de Matt:


Parce qu'à grande échelle, le monorepositaire résoudra tous les mêmes problèmes que le polyrepositaire résout, mais en même temps vous provoquant une forte cohérence de votre code et nécessitant des efforts incroyables pour augmenter l'évolutivité de votre système de contrôle de version.


Vous devez résoudre les mêmes problèmes, que vous choisissiez un mono-référentiel ou un poly-référentiel. Comment publiez-vous les versions? Quelle est votre approche des mises à jour? Rétrocompatible? Dépendances entre projets? Quels styles architecturaux sont acceptables? Comment gérez-vous votre infrastructure de génération et de test? La liste est interminable. Et vous les résoudrez tous en grandissant. Il n'y a pas de fromage gratuit.


Je pense que l'argument de Matt est similaire aux vues partagées par de nombreux ingénieurs (et managers) que je respecte. Cela se produit du point de vue de l'ingénieur travaillant sur le composant ou de l'équipe travaillant sur le composant. Vous entendez des choses comme:


  • La base de code est lourde - je n'ai pas besoin de toutes ces poubelles.
  • C'est plus difficile à tester car je dois vérifier toutes ces ordures dont je n'ai pas besoin.
  • Il est plus difficile de travailler avec des dépendances externes.
  • J'ai besoin de mes propres systèmes de contrôle de version virtuels.

Bien sûr, tous ces points sont raisonnables. Cela se produit dans les deux cas - dans le polyrepository j'ai mes propres trucs, en plus de celui qui est nécessaire pour l'assemblage ... J'ai peut-être besoin d'un autre truc aussi. Par conséquent, je crée «simplement» des outils qui extraient l'ensemble du projet. Ou suis-je en train de créer un faux référentiel mono avec des sous-modules. Nous pourrions marcher toute la journée autour de cela. Mais je pense que l'argument de Matt manque la raison principale, que j'ai à peu près retournée en faveur du monorepositaire:


Il provoque la communication et montre des problèmes.


Lorsque nous partageons des référentiels, nous créons de facto un problème de coordination et de transparence. Cela correspond à la façon dont nous pensons aux équipes (en particulier à la façon dont les participants individuels les considèrent): nous sommes responsables d'une composante particulière. Nous travaillons dans un isolement relatif. Les limites sont fixées sur mon équipe et les composants sur lesquels nous travaillons.


Avec la complexité de l'architecture, une équipe ne peut plus la gérer seule. Très peu d'ingénieurs gardent l'ensemble du système dans leur tête. Supposons que vous contrôliez un composant commun A, qui est utilisé par les commandes B, C et D. L'équipe A refactore, améliore l'API et modifie également l'implémentation interne. Par conséquent, les modifications sont incompatibles en amont. Quels conseils donneriez-vous?


  • Trouvez tous les endroits où l'ancienne API est utilisée.
  • Y a-t-il des endroits où la nouvelle API ne peut pas être utilisée?
  • Pouvez-vous réparer et tester d'autres composants pour vous assurer qu'ils ne cassent pas?
  • Ces équipes peuvent-elles vérifier vos modifications dès maintenant?

Veuillez noter que ces questions sont indépendantes du type de référentiel. Vous devrez trouver les équipes B, C et D. Vous devrez leur parler, connaître l'heure, comprendre leurs priorités. Au moins, nous espérons que vous le ferez.


Personne ne veut vraiment faire ça. C'est beaucoup moins amusant que de simplement réparer cette fichue API. Tout cela est humain et confus. Dans le référentiel, vous pouvez simplement apporter des modifications, donner un avis à ceux qui travaillent sur ce composant (probablement pas B, C ou D) et passer à autre chose. Les équipes B, C et D peuvent simplement rester sur leur version actuelle. Ils seront mis à jour lorsqu'ils réaliseront votre génie!


Dans un référentiel unique, la responsabilité est déplacée par défaut. L'équipe A change de composant et, si elle ne fait pas attention, casse immédiatement B, C et D. Cela fait apparaître B, C et D à la porte A, se demandant pourquoi l'équipe A a cassé l'assemblage. Cela apprend à A qu'ils ne peuvent pas sauter ma liste ci-dessus. Ils devraient parler de ce qu'ils vont faire. B, C et D peuvent-ils se déplacer? Et si B et C le pouvaient, mais D était étroitement lié à un effet secondaire de l'ancien algorithme?


Ensuite, nous devons parler de la façon dont nous sortons de cette situation:


  1. Prise en charge de plusieurs API internes, tandis que l'ancien algorithme sera marqué comme obsolète jusqu'à ce que D puisse cesser de l'utiliser.
  2. Prise en charge de plusieurs versions de versions, une avec l'ancienne interface, une avec la nouvelle.
  3. Diffusion différée des modifications apportées à A jusqu'à ce que B, C et D puissent l'accepter.

Supposons que nous sélectionnons 1, plusieurs API. Dans ce cas, nous avons deux morceaux de code. Ancien et nouveau. Assez pratique dans certaines situations. Nous renvoyons l'ancien code, le marquons comme obsolète et convenons d'un calendrier pour sa suppression avec la commande D. Il est essentiellement identique pour poly et pour le mono-référentiel.


Pour sortir plusieurs versions, nous avons besoin d'une branche. Nous avons maintenant deux composants - A1 et A2. Les équipes B et C utilisent A2 et D utilise A1. Nous avons besoin que chaque composant soit prêt à être publié, car avant que D puisse continuer, des mises à jour de sécurité et d'autres corrections de bugs peuvent être nécessaires. Dans le référentiel, nous pouvons le cacher dans une branche de longue durée qui fait du bien. Dans le mono-référentiel, nous forçons le code dans le nouveau module. L'équipe D devra encore apporter des modifications à l '"ancien" composant. Tout le monde peut voir le coût que nous payons ici - nous avons maintenant deux fois plus de code, et toutes les corrections de bogues qui s'appliquent à A1 et A2 devraient être appliquées aux deux. Avec l'approche consistant à utiliser des branches dans un référentiel, cela est caché derrière la sélection. Nous considérons le coût comme moindre car il n'y a pas de duplication. D'un point de vue pratique, le coût est le même: vous allez créer, publier et maintenir deux bases de code, fondamentalement identiques, jusqu'à ce que vous puissiez en supprimer une. La différence est que dans le monorepositaire, cette douleur est directe et visible. C'est encore pire et bon.


Enfin, nous sommes arrivés au troisième point. Délai de libération. Il est possible que les modifications apportées par A améliorent la vie de l'équipe A. C'est important, mais pas urgent. Pouvons-nous simplement tenir le coup? Dans le référentiel, nous poussons cela pour consolider l'artefact. Bien sûr, nous parlons de cette équipe D. Restez sur l'ancienne version jusqu'à ce que vous rattrapiez! Cela met en place un jeu lâche. L'équipe A continue de travailler sur son composant, ignorant le fait que l'équipe D utilise une version de plus en plus obsolète (c'est un problème pour l'équipe D, ils sont stupides). Pendant ce temps, l'équipe D parle mal de l'attitude imprudente de l'équipe A envers la stabilité du code, si elle en parle. Les mois passent. Enfin, l'équipe D décide de jeter un œil à l'option de mise à niveau, mais il n'y a que plus de changements à A. L'équipe A se souvient à peine quand et comment elle a cassé D. La mise à jour est plus douloureuse et prendra plus de temps. Ce qui l'envoie plus bas dans la pile prioritaire. Jusqu'à ce jour, jusqu'à ce que nous ayons un problème de sécurité en A, ce qui nous oblige à faire une succursale. L'équipe A doit remonter le temps, trouver le moment où D était stable, y corriger le problème et le préparer pour la sortie. C'est le choix de fait que les gens font et c'est de loin le pire. Cela semble être bon pour les équipes A et D, tant que nous pouvons nous ignorer.


Dans le monorepositaire, le troisième n'est vraiment pas une option. Vous êtes obligé de gérer la situation de deux manières. Vous devez voir les coûts d'avoir deux branches de publication. Découvrez comment vous protéger des mises à jour qui rompent la compatibilité descendante. Mais l'essentiel: vous ne pouvez pas éviter une conversation difficile.


D'après mon expérience, lorsque les équipes deviennent grandes, il n'est plus possible de garder l'ensemble du système à l'esprit, et c'est la partie la plus importante. Vous devez améliorer la visibilité des désaccords dans le système. Vous devez travailler activement pour que les équipes quittent les yeux de leurs composants et regardent le travail des autres équipes et des consommateurs.


Oui, vous pouvez créer des outils qui tenteront de résoudre le problème des polyrepositoires. Mais mon expérience dans l'enseignement de la livraison continue et de l'automatisation dans les grandes entreprises me dit ce qui suit: le comportement par défaut sans utiliser d'outils supplémentaires est le comportement que vous attendez. Le comportement par défaut du référentiel est l'isolement, c'est tout l'intérêt. Le comportement par défaut d'un mono-référentiel est la responsabilité partagée et la transparence, c'est tout l'intérêt. Dans les deux cas, je vais créer un outil qui lissera les angles vifs. En tant que leader, je choisirai à chaque fois un mono-référentiel, car les instruments doivent renforcer la culture que je veux, et la culture vient de petites décisions et du travail quotidien de l'équipe.

Source: https://habr.com/ru/post/fr453958/


All Articles