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
L3
e attendi la revisione - unisci la modifica
- crea una nuova versione di
L3
- inizia a lavorare sulla funzione in
P1
facendo 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?