Cos'è il trunk based development? Guida pratica per SRE

Riassunto

Il trunk based development è un modello di branching in cui gli sviluppatori integrano piccole modifiche in un unico trunk condiviso almeno una volta al giorno, mantenendolo sempre rilasciabile. Il lavoro incompleto resta spento dietro feature flag o branch by abstraction. Il modello richiede CI veloce, test affidabili e osservabilità legata a ogni release. DORA lo associa a migliori performance di delivery. Funziona male quando la build è lenta o quando le release versionate richiedono branch di manutenzione di lunga durata.

Un tronco d'albero con rami corti che confluiscono di nuovo nel tronco, immagine del trunk based development

Sono le 3 di notte e il branch di release non fa merge. Quaranta commit, tre settimane di vita, gli stessi file del main. Per capire cos'è il trunk based development partiamo da questo problema: è un modello di branching in cui tutti integrano piccole modifiche in un unico branch condiviso, chiamato trunk o main, almeno una volta al giorno, mantenendo quel branch sempre rilasciabile.

Questa guida è pensata per platform engineer che conoscono già Git. Spiega cosa richiede il modello, da quali rischi protegge e dove, in silenzio, si rompe.

Cosa significa, in termini operativi?

Riduciamo la definizione ai suoi vincoli. Tutti gli sviluppatori integrano in un solo branch. Ogni altro branch vive per ore, non per settimane. Il trunk compila e supera i test a ogni commit, quindi può essere rilasciato in qualsiasi momento.

La pagina delle capacità di DORA fissa dei numeri: tre branch attivi o meno nel repository, merge sul trunk almeno una volta al giorno, nessun code freeze e un ciclo di build e test che dura pochi minuti. Quei numeri sono il punto. Il modello è un budget di feedback loop, non una preferenza di stile sui branch.

Una scrivania buia di notte con un terminale aperto sul portatile e un avviso luminoso sul telefono

I team piccoli a volte committano direttamente sul trunk. I team più grandi usano branch di breve durata e pull request per la review e i controlli di build, ma mai per trattenere il lavoro fuori dall'integrazione. Il sito di riferimento trunkbaseddevelopment.com descrive entrambe le modalità e cita Google, che fa lavorare circa 35.000 sviluppatori su un unico trunk in un monorepo.

Perché i branch di lunga durata falliscono secondo un calendario prevedibile

Un branch di feature è un prestito. Gli interessi sono i conflitti di merge, e crescono a ogni commit che atterra sul main mentre il branch è fuori. Più a lungo vive il branch, più grande è il diff, e più grande è il diff, meno qualcuno lo legge con attenzione.

Ecco cosa succede al tasso di fallimento dei cambiamenti. Un merge da 2.000 righe viene scorso e approvato. Un merge da 60 righe viene letto. I reviewer non sono pigri: razionano l'attenzione. I batch piccoli sono l'unico meccanismo che scala la qualità della review.

Il secondo costo è invisibile fino al rilascio. Due branch che superano ciascuno la CI possono comunque rompersi a vicenda quando si incontrano. Lo si scopre al momento dell'integrazione, il peggiore possibile, con una scadenza addosso.

Di cosa ha bisogno il trunk prima di potersi fidare

Passare al trunk senza le pratiche di supporto è il modo in cui i team tornano ai branch di feature entro un trimestre. Servono quattro cose, prima di tutto.

Se ne manca una, il trunk diventa il posto in cui i guasti si accumulano, invece di quello in cui vengono intercettati.

Come unire lavoro incompleto senza rilasciarlo?

È la domanda che ogni scettico fa, e la risposta è poco spettacolare: si separa il deploy dal rilascio. Il codice arriva in produzione spento, e un flag decide chi lo vede.

// checkout.ts
import { flags } from "./flags";

export async function renderCheckout(user: User) {
  if (await flags.isEnabled("new-payment-flow", { userId: user.id })) {
    return renderNewPaymentFlow(user); // unito sul trunk, spento di default
  }
  return renderLegacyCheckout(user);
}

Il nuovo flusso viene unito il primo giorno, dietro un flag disattivato. Si accende per gli utenti interni, poi per l'1 per cento, poi per il 10, con un gate sugli SLO a ogni passaggio. Se il tasso di errore supera il budget, il flag si spegne e la modifica viene di fatto annullata senza un nuovo deploy. I team che valutano servizi di feature flag gestiti spesso partono da LaunchDarkly, anche se il pattern funziona con qualunque provider o con uno store di configurazione interno.

La seconda tecnica è il branch by abstraction, per i grandi refactor. Si introduce un'interfaccia, si fanno passare i chiamanti da lì, si costruisce la nuova implementazione dietro di essa e si sostituisce quando è pronta. Nessun branch di lunga durata, nessun merge big-bang.

Una fila di interruttori a muro, alcuni accesi e altri spenti, come feature flag

Il costo, però, va detto con onestà. Ogni flag aggiunto è un condizionale in produzione che qualcuno deve rimuovere. Si sono visti team di 80 ingegneri portarsi dietro qualche centinaio di flag stantii, ciascuno un ramo di codice che nessuno può testare fino in fondo.

Il modo più sano è trattare i flag come elementi a vita breve per contratto. A ogni flag si assegnano un responsabile e una data di scadenza al momento della creazione. Si genera un alert sui flag oltre la scadenza. Il percorso di codice morto si elimina nella stessa pull request che rimuove il flag.

Anche il rischio combinatorio è reale. Dieci flag booleani indipendenti danno 1.024 configurazioni possibili, e non si testeranno mai tutte. Conviene mantenere poche interazioni tra flag e tenere separati i flag di release dagli interruttori operativi di lunga durata, come i kill switch.

Strategie di release e osservabilità

Ci sono due modi comuni per tagliare una release dal trunk, e nessuno richiede un branch di lunga durata.

La scelta dipende dal tempo di rilevazione. Se un deploy difettoso si nota in cinque minuti e si fa revert in due, si corregge in avanti. Se il mean time to detect è di un'ora, un branch di release offre un punto d'appoggio.

Binari secondari brevi che confluiscono in un'unica linea ferroviaria principale

L'osservabilità è l'altra metà del patto. Il trunk based development aumenta la frequenza dei deploy, e quindi il numero di momenti in cui qualcosa può andare storto. Il modello funziona solo se una regressione si vede entro pochi minuti da un merge. Servono tassi di errore, percentili di latenza e saturazione legati a una release specifica, non a una dashboard che qualcuno controlla il lunedì.

Un test utile: dopo un merge, si riesce a rispondere a "questa modifica ha spostato il p99 o il tasso di errore?" senza aprire cinque schede? Se no, non si è pronti a unire dieci volte al giorno.

Qualunque sia lo stack, il requisito è lo stesso: marcatori di deploy sui grafici, alert di burn rate sugli SLO e un percorso di revert che una persona stanca possa eseguire alle 3 di notte senza ragionarci.

Trunk based development contro GitFlow e GitHub Flow

Questi tre modelli si confondono spesso, quindi conviene distinguerli con una proprietà: per quanto tempo il lavoro resta fuori dal branch condiviso.

Se il team usa già GitHub Flow con branch che vivono meno di due giorni, è più vicino al trunk di quanto pensi. Il divario sta di solito nella disciplina sui flag e nella latenza della review, non negli strumenti.

Quando il trunk based development è la scelta sbagliata

Conviene rinviarlo, o evitarlo, in alcuni casi. Va riconosciuto con onestà in quale di questi ci si trova.

Il trunk based development non garantisce niente. Il risultato di DORA è una correlazione dai dati del 2016 e del 2017, con team che seguono queste pratiche e mostrano performance di delivery e operative migliori. Non dice che rinominare i branch sistema la pipeline.

Come partire senza una migrazione big-bang

Conviene non annunciare nessuna policy. Prima si misura, poi si restringe. Le metriche DORA vanno strumentate prima di cominciare, altrimenti si discute a sensazione.

  1. Registrare la durata attuale dei branch e la dimensione mediana delle pull request. Sono la baseline.

  2. Fissare un limite, per esempio nessun branch più vecchio di due giorni, e renderlo visibile su una dashboard.

  3. Sistemare la parte più lenta della CI. Se la build dura 30 minuti, nient'altro conta ancora.

  4. Introdurre un flag per una funzionalità reale. Rilasciarla spenta e rimuovere il flag entro uno sprint.

  5. Eliminare i code freeze per ultimi, quando il percorso di revert è stato provato sul campo.

Conviene rivalutare dopo un mese. I numeri che si muovono per primi sono di solito la dimensione delle pull request e il tempo di merge. Il tasso di fallimento dei cambiamenti richiede più tempo e a volte peggiora prima di migliorare, perché i guasti emergono finalmente prima.

Cosa dirà il prossimo post-mortem del modello di branching? Rivedere gli ultimi tre incidenti aiuta: quanti dipendevano da un'integrazione grande e tardiva? Quel conteggio è il caso d'affari più solido che si possa presentare.

Domande frequenti

Cos'è il trunk based development in una frase?
È una pratica di controllo versione in cui gli sviluppatori uniscono piccole modifiche in un unico branch condiviso almeno una volta al giorno, tengono ogni altro branch vivo al massimo per poche ore e mantengono il trunk sempre rilasciabile.
In cosa il trunk based development si differenzia da GitFlow?
GitFlow isola il lavoro su branch di feature, develop e release per giorni o settimane. Il trunk based development integra in modo continuo e nasconde il lavoro incompleto dietro feature flag o astrazioni, invece che dietro branch.
Servono i feature flag per il trunk based development?
Non per ogni modifica, ma serve un modo per unire in sicurezza il lavoro incompleto. Feature flag e branch by abstraction sono le due tecniche standard, e i flag offrono anche rilasci progressivi e uno spegnimento rapido.
Quanto deve durare un branch nel trunk based development?
Secondo DORA i branch durano in genere al massimo qualche ora, con merge sul trunk almeno una volta al giorno e tre o meno branch attivi nel repository.
Funziona anche per i team grandi?
Sì. I team più grandi usano branch di breve durata per review e controlli di build, insieme a flag e astrazioni. Il sito di riferimento cita Google, con circa 35.000 sviluppatori su un unico trunk in un monorepo.
Quando non conviene adottarlo?
Quando la CI è troppo lenta per girare a ogni merge, quando manca un meccanismo per nascondere il lavoro incompleto o quando il team non si fida della suite di test. Conviene sistemare prima questi punti.