Go with the (git)flow, or die debating — IT
Tutti quanti versioniamo le nostre codebase sfruttando uno dei sistemi di controllo di versione presenti sul mercato. Da quando ho iniziato a lavorare per wellD ne ho visti cambiare tre.
All’inizio si usava CVS, ma non essendo transazionale (all or nothing) c’era da divertirsi quando qualcosa si piantava a metà — per non parlare delle sue performance.
Il passo successivo fu quindi SVN (si è scelto di non andare su git perché troppo complicato!) e ci siamo trovati con il mal di testa ogni volta che si doveva fare il merge tra due branch.
Finalmente arrivò il tempo di git, che sconvolse in positivo il modo di lavorare del team di sviluppo.
A differenza dei precedenti però, git richiede di definire delle modalità condivise di uso del sistema.
Una delle prime con le quali ci siamo scontrati è stata la necessità di adottare una strategia di gestione dei branch. I tre criteri che hanno guidato la nostra scelta sono: una Continuous Integration facile da integrare, rilasci frequenti e code review puntuale.
Branching strategy
Di seguito una panoramica delle branching strategy che abbiamo sperimentato direttamente o indirettamente, tenendo presente i criteri appena citati.
Anarchy strategy
Anche detta “no strategy”. In questo modello si da infatti la facoltà ad ogni sviluppatore del team di aprire branch senza rispettare alcuna convenzionesulla durata, nome e contenuto del branch.
Il vantaggio (apparente) è un kickoff rapido e l’indipendenza nello sviluppo. Il risultato (ahimé molto più tangibile) è che in poco tempo la situazione diventa ingestibile.
Il team spenderà del tempo per capire qual è il branch attivo di sviluppo, su quale deve sviluppare, in quale/i branch è contenuta una particolare feature o quale branch bisogna portare in produzione.
Git Flow
Per oltre un anno abbiamo abbracciato git-flow, che ci ha dato ordine, metodo e struttura, oltre che una CLI che ha ovviato ai problemi di naming convention.
L’idea che c’è dietro a git-flow è abbastanza semplice:
- il branch master contiene i tag con tutte le versioni rilasciate
- develop gli sviluppi aperti
- i branch di feature le nuove funzionalità in fase di sviluppo
- i branch di release le versioni dal nostro software candidate al rilascio
- i branch di hotfix la correzioni dei bug segnalati dalla produzione
Il problema più grande del GitFlow è la sua complessità. Tutto lo sviluppo non è più contenuto su master
, default branch per molti tool, ma su develop,
conmaster
che viene utilizzato come contenitore dei rilasci.
Questo tipo di approccio renderà di fatto impossibile fare code review delle MR (merge request) aperte su master (obbligatorie in quanto protetto), perché conterranno gli sviluppi di settimane/mesi. Vi è comunque la possibilità di aprire MR dai feature
branch su develop
, ma tutto viene lasciato alla buona volontà dello sviluppatore in quanto non è necessaria una MR poiché develop
non è protetto (quanto meno non di default).
Uno dei problemi maggiori che abbiamo riscontrato è la correzione di bug su versioni precedenti all’ultima rilasciata. Ipotizziamo di aver rilasciato la versione 2.X
di un nostro software, e che un cliente ci segnali un problema sulla versione 1.Z
. La cosa più ragionevole da fare è posizionarsi sumaster
al tag 1.Z
e aprire un branch di support
per correggere il bug. Una volta corretto il bug come ci comportiamo? Da quale branch rilasciamo (support, master, altro?) la nostra correzione? Dove integriamo il nostro fix? Lo riportiamo su master? Su develop? O lasciamo il branch di support
appena aperto unmerged
per sempre?
La procedura di rilascio nel GitFlow può diventare davvero ostica. Le fasi che la compongono sono:
- aprire il branch di release da
develop
, con il comandogit flow release start <VERSION_NUMBER>
- aggiornare la code base (changelog, project version, readme etc..)
- attendere che la CI (continuous integration) ci dia l’OK per chiedere la MR su
master
- unire il branch di release a
master
,git flow release finish <VERSION_NUMBER>
Alla fine di questa fase avremo integrato il branch direlease
sumaster
e sudevelop
, inoltre il commit di rilascio saràtaggato
e noi ci troveremo nuovamente sudevelop
- fare push di
master
,develop
e deltag
(N.B.git push --tags
) - P.S. aggiornare la versione del progetto sul branch di
develop
per la prossima iterazione e solo dopo fare push.
In alcuni casi è davvero overkill eseguire questa procedura, e commettere un errore (per esperienza) è abbastanza facile.
GitHub Flow
Il GitHub Flow è semplice e pulito, si hanno solo 2 tipi di branch: il master e i branch di feature. Lo sviluppatore crea un branch dal repository e svilupperà su di esso la nuova feature, e una volta terminata aprirà una MR (sottoposta a code review) su master
. Per aggiornare una MR basta fare un push sul proprio branch, e quando la MR sarà pronta ne verrà fatto il merge
su master
, dopodiché il branch di feature
verrà eliminato.
Questa branching strategy fa però 2 forti assunzioni:
- Che si possa andare in produzione in qualsiasi momento, cosa non sempre vera. Molti nostri clienti ci danno una finestra oraria di un giorno ben preciso per portare la nuova versione del software in produzione.
- Il cliente installerà sempre l’ultima versione del nostro applicativo e quindi non ci sarà bisogno di fare supporto e bug fixing su rilasci antecedenti all’ultimo. 🤔
Gitlab Flow 🚀
Partendo dal GitHub Flow è stata pensata una nuova strategia, che prevede un branch per ogni ambiente. Supponiamo di avere un ambiente di staging, uno di QA (Quality Assurance) e uno di Prod e che ognuno di essi venga aggiornato in maniera automatica da uno script di CI che punta al rispettivo branch.
In questo modo abbiamo tutti i vantaggi di MR piccole e puntuali su master
, dove possiamo effettuare code review, e quando pronti possiamo portare i nostri cambiamenti sull’ambiente di QA (MR da master
a pre-production
) e successivamente in Prod (MR da pre-production
a production
).
Questo workflow ci assicura inoltre che tutti i commit siano testati su tutti gli ambienti.
Talvolta è necessario mantenere attivo lo sviluppo su più versioni del nostro software, per questo motivo si ricorre ai Release Branches.
In questo caso ogni branch corrisponde ad una versione del nostro software (nel caso dell’immagine sopra una minor). Si tende a creare il branch di rilascio il più tardi possibile (stable nell’immagine), per evitare di dover applicare hotfix su più branch.
Quando viene riscontrata un’anomalia nel nostro software, la correzione viene fatta su branch di feature
che poi viene integrato in master
e successivamente viene fatto il cherry-pick del commit sui branch di rilascio. Questa tecnica, chiamata Upstream First, viene impiegata per evitare che ci si dimentichi di integrare l’hotfix su master e che quindi il problema si ripresenti su una futura release.
Conclusione 🤓
Uno dei concetti che più approvo della metodologia Lean è il continuo miglioramento, che viene perfettamente racchiuso in questa storiella:
C’era il titolare di una grossa azienda americana che aveva deciso di ispirarsi alla Lean per migliorare la propria catena produttiva. Dopo alcuni mesi che era stata introdotta in azienda, invitò il proprietario di una fabbrica giapponese (dove da Lean era stata applicata da sempre) a visitare il proprio stabilimento.
Il giorno prefissato per l’appuntamento l’americano fa da cicerone al giapponese durante la gita alla fabbrica, mostrandogli le migliorie introdotte grazie alla Lean. Il giapponese guarda ma non si sbilancia in nessun commento.
Alla fine del tour l’americano chiede al giapponese: “Cosa ne pensa di come abbiamo riadattato lo stabilimento seguendo le vostre line guida? Siamo riusciti ad adottare in pieno la metodologia Lean?”
Il giapponese rispose: “Io questo non lo so, ieri non c’ero!”
Questo per dire che ad oggi, partendo dal CVS, siamo arrivati al git e al Gitlab Flow, che al momento sembra soddisfare in modo abbastanza completo il nostro ciclo di sviluppo, ma siamo sempre pronti a sperimentare ed eventualmente adottare qualsiasi tool / strategia che migliori la vita del team di sviluppo.
Riferimenti 🔗
- https://docs.gitlab.com/ee/workflow/gitlab_flow.html
- https://datasift.github.io/gitflow/IntroducingGitFlow.html
- https://danielkummer.github.io/git-flow-cheatsheet/
- Esempio di GitLab Flow: https://github.com/spring-projects/spring-boot
- Upstream first: https://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first
—
Matteo Codogno, Technical Leader @WellD