Nous faisons des projets, mais nous réutilisons beaucoup de code entre les projets et avons beaucoup de bibliothèques qui contiennent notre code commun. Au fur et à mesure que nous implémentons de nouveaux projets, nous trouvons plus de façons de factoriser le code commun et de le mettre dans les bibliothèques. Les bibliothèques dépendent les unes des autres et les projets dépendent des bibliothèques. Chaque projet et toutes les bibliothèques utilisées dans ce projet doivent utiliser la même version de toutes les bibliothèques auxquelles ils se réfèrent. Si nous publions un logiciel, nous devrons corriger des bugs et peut-être ajouter de nouvelles fonctionnalités pendant de nombreuses années, parfois pendant des décennies. Nous avons environ une douzaine de bibliothèques, les changements se répartissent souvent sur plus de deux et plusieurs équipes travaillent sur plusieurs projets en parallèle, apportant des modifications simultanées à toutes ces bibliothèques.
Nous avons récemment basculé vers git et mis en place des référentiels pour chaque bibliothèque et chaque projet. Nous utilisons stash comme référentiel commun, faisons de nouvelles choses sur les branches de fonctionnalités, puis faisons des demandes d'extraction et ne les fusionnons qu'après examen.
Bon nombre des problèmes que nous devons traiter dans les projets nous obligent à apporter des modifications à plusieurs bibliothèques et au code spécifique du projet. Il s'agit souvent de modifications des interfaces de bibliothèque, dont certaines sont incompatibles. (Si vous pensez que cela semble louche: nous nous connectons avec du matériel et cachons du matériel spécifique derrière des interfaces génériques. Presque chaque fois que nous intégrons du matériel d'un autre fournisseur, nous rencontrons des cas que nos interfaces actuelles n'anticipaient pas, et nous devons donc les affiner.) Pour exemple, imaginez un projet en P1utilisant les bibliothèques L1, L2et L3. L1utilise également L2et L3, et L2utilise L3également. Le graphique des dépendances ressemble à ceci:
<-------L1<--+
P1 <----+ ^ |
<-+ | | |
| +--L2 |
| ^ |
| | |
+-----L3---+
Imaginez maintenant qu'une fonctionnalité de ce projet nécessite des modifications dans P1et L3qui modifient l'interface de L3. Ajoutez maintenant des projets P2et P3dans le mix, qui font également référence à ces bibliothèques. Nous ne pouvons pas nous permettre de les passer tous à la nouvelle interface, d'exécuter tous les tests et de déployer le nouveau logiciel. Alors, quelle est l'alternative?
- implémenter la nouvelle interface dans
L3 - faire une demande d'extraction
L3et attendre l'examen - fusionner le changement
- créer une nouvelle version de
L3 - commencer à travailler sur la fonctionnalité en la
P1faisant référence àL3la nouvelle version de, puis implémenter la fonctionnalité surP1la branche de fonctionnalité de - faire une demande d'extraction, la faire examiner et fusionner
(Je viens de remarquer que j'ai oublié de passer L1et L2de la nouvelle version. Je ne sais même pas où coller cela dans, car il devrait être fait en parallèle avec P1...)
Il s'agit d'un processus fastidieux, sujet aux erreurs et très long pour implémenter cette fonctionnalité, il nécessite des examens indépendants (ce qui le rend beaucoup plus difficile à examiner), ne se modifie pas du tout et est susceptible de nous mettre hors service parce que nous être tellement embourbé dans le processus que nous ne faisons rien.
Mais comment utiliser la ramification et le balisage afin de créer un processus qui nous permet d'implémenter de nouvelles fonctionnalités dans de nouveaux projets sans trop de frais généraux?