Stiamo facendo progetti, ma riutilizziamo molto codice tra i progetti e abbiamo molte librerie che contengono il nostro codice comune. Mentre implementiamo nuovi progetti, troviamo più modi per calcolare il codice comune e inserirlo nelle librerie. Le librerie dipendono l'una dall'altra e i progetti dipendono dalle librerie. Ogni progetto e tutte le librerie utilizzate in quel progetto devono utilizzare la stessa versione di tutte le librerie a cui si riferiscono. Se rilasciamo un software, dovremo risolvere bug e magari aggiungere nuove funzionalità per molti anni, a volte per decenni. Abbiamo circa una dozzina di librerie, i cambiamenti spesso vengono tagliati su più di due e diversi team lavorano su più progetti in parallelo, apportando modifiche simultanee a tutte queste librerie.
Recentemente siamo passati a git e abbiamo creato repository per ogni libreria e ogni progetto. Usiamo lo stash come un repository comune, facciamo cose nuove sui branch delle feature, quindi eseguiamo le richieste pull e le uniamo solo dopo la revisione.
Molti dei problemi che dobbiamo affrontare nei progetti ci impongono di apportare modifiche a diverse librerie e al codice specifico del progetto. Questi includono spesso modifiche alle interfacce della libreria, alcune delle quali sono incompatibili. (Se pensate che questo suoni sia di pesce: ci interfacciamo con l'hardware e nascondiamo hardware specifico dietro interfacce generiche: quasi ogni volta che integriamo l'hardware di altri fornitori ci imbattiamo in casi che le nostre attuali interfacce non hanno previsto, e quindi dobbiamo perfezionarli.) Per esempio, immagina un progetto P1 usando le librerie L1 , L2 e L3 . L1 usa anche L2 e L3 , e L2 usa anche L3 . Il grafico delle dipendenze ha il seguente aspetto:
<-------L1<--+
P1 <----+ ^ |
<-+ | | |
| +--L2 |
| ^ |
| | |
+-----L3---+
Ora immagina che una funzione per questo progetto richieda modifiche in P1 e L3 che cambiano l'interfaccia di L3 . Ora aggiungi i progetti P2 e P3 nel mix, che si riferiscono anche a queste librerie. Non possiamo permetterci di passare tutti alla nuova interfaccia, eseguire tutti i test e implementare il nuovo software. Quindi qual è l'alternativa?
- implementa la nuova interfaccia in
L3 - fai una richiesta di pull per
L3e attendi la revisione - unisci la modifica
- crea una nuova versione di
L3 - inizia a lavorare sulla funzione in
P1facendo riferimento alla nuova versione diL3, quindi implementa la funzione sul ramo di funzione diP1 - fai una richiesta di pull, fai revisionare e unire
(Ho appena notato che ho dimenticato di passare L1 e L2 alla nuova versione e non so nemmeno dove posizionarlo, perché dovrebbe essere fatto in parallelo con P1 ...)
Questo è un processo noioso, soggetto a errori e molto lungo per implementare questa funzione, richiede revisioni indipendenti (il che rende molto più difficile la revisione), non è affatto ridimensionabile e rischia di farci uscire affari perché siamo così impantanati nel processo che non otteniamo mai nulla.
Ma come impiegare la ramificazione e il tagging per creare un processo che ci permetta di implementare nuove funzionalità in nuovi progetti senza troppi sovraccarichi?