Ormai nella quasi totalità degli annunci di lavoro, le aziende dichiarano di fare Continuous Integration (CI) – poi però entri in azienda e scopri che usano GitFlow, i Feature Branches o qualunque altro tipo di approccio che prevede branch isolati che durano giorni/settimane.
Cosa centra, direte voi? Centra molto, perchè CI sembra essere diventato sinonimo di “avere una pipeline”, ma non è questo il suo significato.
Cosa significa Continuous Integration?
Continuous Integration significa mandare molto di frequente il lavoro sul branch principale (trunk/master) per verificare, al meglio delle nostre conoscenze, che il lavoro sia rilasciabile.
Voglio sottolineare sopratutto due aspetti:
- molto di frequente: il nostro codice deve andare spesso su master, e anche se ancora non abbiamo un indicazione numerica, è facile intuire che un branch che dura giorni/settimane non rispetti questa caratteristica
- rilasciabile: quando mergiamo il nostro codice, non lo stiamo “semplicemente” integrando col resto; vogliamo farlo per verificare che le nostre modifiche non abbiano causato problemi, e che quindi il branch principale rimanga in uno stato che consideriamo “rilasciabile”
Ci sono vari motivi per cui implementare la CI è fondamentale:
- Solo il 20% delle features è usato spesso: nel restante 80%, il 56% sono usate raramente, e il 24% mai usate; nonostante sia difficile ammetterlo, le feature sono una fonte di spreco e dovremmo limitarle il più possibile a quelle davvero necessarie.
- Prima scopriamo un bug, meno ci costa: il costo di un bug cresce in modo esponenziale; rispetto allo sviluppo, un bug emerso in QA costa 5 volte tanto, che diventa 100 volte tanto in produzione. Anticipare la scoperta dei bug riduce i nostri costi di sviluppo drasticamente.
- Riduciamo l’impatto dell’unplanned work: c’e una parte di lavoro che non possiamo prevedere (bug, reword, incidents, etc) ma possiamo prevenire; il libro Accelerate ci ha insegnato come i team performanti riescano a ridurre il tempo investito in unplanned work dal 27 al 20%.
- Andare over-budget è meglio che essere in ritardo: un progetto in ritardo ma in budget perde il 30% in più di ricavi nei 5 anni successivi rispetto ad uno in over-budget ma lanciato in tempo.
Tutti questi punti ci devono far capire che il nostro obiettivo deve essere prioritizzare il lead time, non la creazione di nuove feature. Il lead time è il tempo che passa dalla nascita dell’idea fino al rilascio in produzione.
Fai commit su master!
Ma allora, cosa significa nel pratico fare Continuous Integration? Le attività minime che un team deve mettere in pratica per poter dire di fare CI sono:
- Codice integrato sul branch principale almeno una volta al giorno
- Test suite eseguita con successo prima di integrare le nostre modifiche sul branch principale, per assicurare che le nuove modifiche siano funzionanti
- Test suite eseguita con successo dopo aver integrato le nostre modifiche sul branch principale, per assicurare che l’integrazione sia andata a buon fine
- Tutto il lavoro sulle features si ferma se la build del branch principale si rompe
- Le modifiche/aggiunte al codice non devono rompere il codice esistente
Ricordate il “molto di frequente” di inizio articolo? Ora abbiamo un riferimento preciso: almeno una volta al giorno. Questo significa che abbiamo solo due modi per rispettare questa richiesta:
- Fare commit direttamente su master
- Creare branch che durano al massimo 24 ore
Questa pratica prende il nome di Trunk-Based Development, che possiamo definire come una pratica nella quale le modifiche al codice vengono integrate direttamente sul branch principale, senza nessun branch di lavoro nel mezzo (Feature Branches, Develop/Test, ecc.).
Lasciamo le PR asincrone per l’Open Source
Come per tante pratiche Agili, per fare Trunk-Based dobbiamo abbracciare il cambiamento (“embrace change”) accettando che potrebbe esistere un modo più efficace di fare le cose rispetto a quello a cui siamo abituati.
Il cambiamento che fa soffrire di più i dev che faticano a pensare al TBD come pratica utile è quello di committare su master, o quantomeno avere un approccio più snello alle Pull Requests se manteniamo i branch: se vogliamo integrare il codice almeno una volta al giorno, se non più volte al giorno, non possiamo pensare di lavorare con le PR asincrone per cui a volte attendiamo ore/giorni per riuscire a superare la review.
Ma è davvero un problema?
Le PR asincrone sono quasi uno standard de facto nel mondo software odierno, che è stato mutuato dal mondo Open Source – ma il contesto OSS rispetto a quello di un team in un azienda è molto, molto diverso!
Senza dilungarmi troppo, lo schema seguente evidenzia queste differenze:
In generale, siamo troppo abituati a pensare ad ognuno di noi come “individual contributor”, un termine che non mi piace per niente, e troppo poco a pensarci invece “membri di un team” che quindi dovrebbero collaborare in modo molto più stretto e frequente per ottenere un risultato migliore che solo un collettivo può ottenere.
Come rilasciamo un lavoro incompleto?
Il secondo enorme dilemma che i programmatori hanno pensando al Trunk-Based è legato alla domanda: “come facciamo a rilasciare un lavoro incompleto?”.
Dico rilasciare perchè abbiamo visto che l’obiettivo di integrare su master è verificare che tutto sia rilasciabile, quindi anche se fare CI non implica strettamente fare Continuous Delivery/Deployment, dobbiamo comunque assicurarci che sia pronto al rilascio.
Le feature che sviluppiamo però sono complicate, durano settimane proprio per questo, quindi come si fa a mandare su master codice rilasciabile ogni giorno?
In primis, vi invito a riflettere ed essere molto più critici su questo tema: forse le feature sono troppo grandi? Ricordate che le feature sono uno spreco, meglio ragionare in termini di MVP: rilasciare la versione più semplice possibile per verificare che la feature venga usata, e poi nel caso investirci altro tempo. Ma questo è argomento che meriterebbe un articolo tutto per se!
Riguardo al rilasciare lavoro incompleto, invece, vi stupirà forse, ma è un problema ampiamente risolto: semplicemente, dobbiamo per l’ennesima volta abbracciare un modo differente di pensare. Purtroppo, tutti i dev impararano a rilasciare feature complete, pensando che sia l’unico modo possibile di lavorare – ed è una delle abitudini più difficili da lasciarsi alle spalle, ma è possibile!
Come dicevo, è un problema risolto ed esistono vari Design Pattern implementativi che si possono usare per rilasciare un codice incompleto che non sia visibile per l’utente finale, come ad esempio i Feature Flags, oppure semplicemente fare in modo che l’interfaccia che l’utente vede sia l’ultimo elemento rilasciato. Vi lascio un link all’articolo di Martin Fowler in merito, che spiega il tema molto meglio di quanto potrei mai fare io: lo trovate qui.
Vi invito, per concludere, a riflettere sui benefici di questa pratica: grazie all’abilità di rilasciare continuamente lavoro non completo, siamo ora in grado di separare il rilascio del software dal lancio della feature a livello marketing, togliendo la necessità di sincronizzazione diretta con il team marketing stesso, o con gli operativi che dovranno magari dare supporto. Ci permette anche di gestire molto più facilmente un rilascio iterativo ed incrementale, con cui magari rendere visibile il codice solo ad alcuni utenti per verificarne gli impatti in modo controllato.
Insomma, la pratica del Trunk-Based Development ci spaventa perchè ci richiede di lasciarci molte abitudini alle spalle e la resistenza al cmabiamento fa parte della natura umana – ma la potenza della pratica ripaga lo sforzo: dategli una chance!
Se questo articolo ti è piaciuto, sappi che è parte di una serie mensile a tema Agile, il che significa che puoi recuperare i precedenti e che ne arriveranno altri nei prossimi mesi! In più, se ti piace il mio stile, dai un occhiata a Learn Agile Practices, il mio ecosistema di contenuti online nei quali parlo di pratiche e metodologie Agile come TDD, CI, CD e molto altro a tema programmazione.
Scopri tutto su learnagilepractices.com e seguimi su LinkedIn!