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 altri modi per scomporre 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 rilasceremo un software dovremo correggere i bug e forse aggiungere nuove funzionalità per molti anni, a volte per decenni. Abbiamo circa una dozzina di biblioteche, le modifiche spesso attraversano più di due e diversi team lavorano su diversi progetti in parallelo, apportando modifiche simultanee a tutte queste librerie.
Di recente siamo passati a git e creato repository per ogni libreria e ogni progetto. Usiamo stash come repository comune, facciamo nuove cose sui rami delle funzionalità, quindi facciamo richieste pull e le uniamo solo dopo la revisione.
Molte delle questioni 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 pensi che questo suona male: 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 in cui le nostre interfacce attuali non prevedevano, e quindi dobbiamo raffinarle.) Per esempio, immaginate un progetto P1
utilizzando le librerie L1
, L2
e L3
. L1
usa anche L2
e L3
, e L2
usa L3
anche. Il grafico delle dipendenze è simile al seguente:
<-------L1<--+
P1 <----+ ^ |
<-+ | | |
| +--L2 |
| ^ |
| | |
+-----L3---+
Ora immagina che una caratteristica di questo progetto richieda cambiamenti P1
e L3
che cambi l'interfaccia di L3
. Ora aggiungi progetti P2
e P3
nel mix, che fanno riferimento anche a queste librerie. Non possiamo permetterci di passare tutti alla nuova interfaccia, eseguire tutti i test e distribuire il nuovo software. Quindi qual è l'alternativa?
- implementare la nuova interfaccia in
L3
- fare una richiesta pull
L3
e attendere la revisione - unisci il cambiamento
- creare una nuova versione di
L3
- iniziare a lavorare sulla funzionalità
P1
facendola riferimento allaL3
nuova versione, quindi implementare la funzionalità sulP1
ramo delle funzionalità - fare una richiesta pull, farla revisionare e fondere
(Ho appena notato che ho dimenticato di passare L1
e L2
alla nuova versione. E non so nemmeno dove attaccarlo, perché dovrebbe essere fatto in parallelo con P1
...)
Si tratta di un processo noioso, soggetto a errori e molto lungo per implementare questa funzione, che richiede revisioni indipendenti (che rendono molto più difficile la revisione), non si ridimensiona affatto e probabilmente ci metterà fuori mercato perché noi impantanarsi così tanto nel processo che non facciamo mai nulla.
Ma come possiamo impiegare la ramificazione e la codifica per creare un processo che ci consenta di implementare nuove funzionalità in nuovi progetti senza troppe spese generali?