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 P1
utilisant les bibliothèques L1
, L2
et L3
. L1
utilise également L2
et L3
, et L2
utilise 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 P1
et L3
qui modifient l'interface de L3
. Ajoutez maintenant des projets P2
et P3
dans 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
L3
et attendre l'examen - fusionner le changement
- créer une nouvelle version de
L3
- commencer à travailler sur la fonctionnalité en la
P1
faisant référence àL3
la nouvelle version de, puis implémenter la fonctionnalité surP1
la branche de fonctionnalité de - faire une demande d'extraction, la faire examiner et fusionner
(Je viens de remarquer que j'ai oublié de passer L1
et L2
de 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?