Git rende economico creare branch, ma non dice come usarli. Ogni team finisce per darsi delle regole, esplicite o implicite: quanti rami esistono, quanto durano, chi li unisce e quando. Una strategia di branching ben scelta riduce i conflitti e rende i rilasci prevedibili; una scelta sbagliata produce rami che vivono per settimane, merge dolorosi e la sensazione che il controllo di versione sia un ostacolo invece che un aiuto.
Il nemico comune: i rami lunghi
Prima di confrontare le strategie, conviene capire il problema che tutte cercano di risolvere. Più a lungo un branch resta separato dal ramo principale, più il codice diverge. Le conseguenze sono note a chiunque abbia lavorato in team:
- conflitti di merge numerosi e difficili da risolvere;
- difetti di integrazione che emergono solo alla fine, quando correggerli costa di più;
- revisioni del codice enormi, che nessuno riesce a leggere con attenzione.
Le diverse strategie sono, in fondo, risposte diverse alla domanda: quanto a lungo possiamo permetterci di lavorare separati?
Trunk-based development
Nel trunk-based development tutti integrano frequentemente sul ramo principale (il "tronco", spesso chiamato main). I branch, se esistono, durano poche ore o al massimo un paio di giorni.
Come si lavora su una funzione che richiede settimane? Con alcune tecniche:
- feature flag: il codice nuovo è presente ma disattivato da un interruttore di configurazione finché non è pronto;
- sviluppo incrementale: la funzione viene divisa in passi piccoli, ognuno integrabile senza rompere nulla;
- branch by abstraction: si introduce un livello di astrazione che permette di sostituire un componente gradualmente.
Il prerequisito è una buona suite di test automatici eseguita a ogni integrazione. Senza test, integrare spesso significa solo rompere spesso.
GitHub flow
Il GitHub flow è una variante leggera e molto diffusa:
mainè sempre in uno stato rilasciabile;- per ogni modifica si crea un branch con un nome descrittivo;
- si apre una pull request, si discute e si esegue la revisione;
- dopo l'approvazione e il passaggio dei test, si unisce in
maine si rilascia.
È adatto a progetti con rilascio continuo, come applicazioni web, dove esiste una sola versione in produzione. La disciplina chiave è mantenere i branch piccoli: un GitHub flow con pull request da tremila righe ha tutti i difetti dei rami lunghi.
Git flow
Git flow è un modello più strutturato, con rami dedicati a ruoli diversi:
| Ramo | Ruolo |
|---|---|
main |
Contiene solo le versioni rilasciate |
develop |
Integra le funzioni in vista del prossimo rilascio |
feature/* |
Sviluppo delle singole funzioni, partono da develop |
release/* |
Preparazione e stabilizzazione di una versione |
hotfix/* |
Correzioni urgenti partendo da main |
Questo modello ha senso quando si mantengono versioni numerate e distinte, per esempio un software installato dai clienti, in cui la versione 2.3 deve ricevere correzioni mentre si sviluppa la 2.4. Per un sito web con un'unica versione online, invece, Git flow aggiunge passaggi senza portare benefici proporzionati: molti team lo hanno abbandonato proprio per questo.
Come scegliere
Alcune domande pratiche orientano la scelta:
- Quante versioni del software devi mantenere contemporaneamente? Una sola: trunk-based o GitHub flow. Diverse: un modello con rami di rilascio.
- Quanto sono affidabili i test automatici? Se sono scarsi, prima di tutto vanno migliorati; l'integrazione frequente senza test è rischiosa.
- Quanto è grande il team? Con molte persone, regole chiare sulla durata dei branch e sulla revisione diventano indispensabili.
- Con che frequenza rilasci? Rilasci quotidiani spingono verso il trunk; rilasci trimestrali tollerano più struttura.
Nel mio lavoro, per progetti web gestiti da team piccoli, il GitHub flow con branch brevi e test obbligatori è quasi sempre il compromesso migliore.
Regole utili qualunque sia la strategia
- Proteggi il ramo principale: nessun push diretto, merge solo dopo test e revisione.
- Nomi descrittivi:
fix/arrotondamento-totalidice più dibranch-prova-3. - Aggiorna spesso il tuo branch rispetto al principale, per scoprire subito i conflitti.
- Cancella i branch uniti: un repository con centinaia di rami morti confonde tutti.
- Scrivi la strategia: due righe nel README evitano discussioni e incomprensioni.
Merge, squash o rebase
Al momento dell'integrazione si sceglie anche come portare i commit nel ramo principale. Il merge conserva tutta la storia del branch con un commit di unione. Lo squash comprime il branch in un solo commit, producendo una storia lineare e compatta. Il rebase riapplica i commit uno per uno in cima al principale, mantenendoli separati ma lineari. Nessuna opzione è giusta in assoluto: conta che il team scelga e la applichi in modo coerente.
In sintesi
- Tutte le strategie combattono lo stesso problema: i rami che vivono troppo a lungo.
- Il trunk-based development integra di continuo e richiede test solidi e feature flag.
- Il GitHub flow è semplice ed efficace per software con una sola versione in produzione.
- Git flow ha senso quando si mantengono più versioni rilasciate in parallelo.
- Qualunque modello scegli, proteggi il ramo principale e mettilo per iscritto.