La lliçó anterior va deixar una pregunta oberta. GitHub Flow demana branques curtes, però no diu quant de curtes, i no ofereix resposta per a la feina que no cap en tres dies.

Trunk Based Development (desenvolupament basat en el tronc, sovint abreujat TBD) respon les dues coses amb una regla que sona gairebé agressiva:

Cada persona de l'equip integra la seva feina al tronc almenys una vegada al dia.

No «quan la funcionalitat estigui llesta». No «quan passi la revisió». Cada dia. Encara que la funcionalitat no estigui acabada. Encara que de moment no serveixi per a res.

La primera reacció de gairebé tothom és la mateixa: això és impossible, com puc integrar codi a mitges a la branca que es desplega a producció? Aquesta objecció és correcta, i respondre-la és el contingut central d'aquesta lliçó. Hi ha un mecanisme que ho fa possible —separar el desplegament de la publicació— i sense entendre'l, TBD sembla una temeritat. Amb ell, és probablement la pràctica que més impacte té sobre la velocitat de lliurament d'un equip.

A gestor-tasques, l'equip ja fa un temps que treballa amb GitHub Flow a la versió al núvol. Funciona, però han detectat un patró: dues o tres vegades al mes, una branca que ha viscut deu dies arriba amb conflictes a app.js que costen mitja jornada de resoldre i que de vegades introdueixen fallades. L'Ana proposa anar un pas més enllà.

Contingut

  1. La idea i d'on ve
  2. Per què l'objectiu real és reduir el temps entre integracions
  3. Les dues variants del model
  4. Separar desplegament de publicació: banderes de funcionalitat
  5. El cost de les banderes: el deute de banderes
  6. Branch by abstraction: canvis grans sense branques llargues
  7. Branques de versió només quan fan falta
  8. Què exigeix TBD
  9. Comparativa final dels tres fluxos
  10. Arbre de decisió: quin triar

  1. La idea i d'on ve

Trunk Based Development no és un model nou: és el més antic dels tres. Abans que Git popularitzés les branques barates, ramificar era car i dolorós —a CVS o Subversion, una branca de dues setmanes era una aventura— i per això tothom treballava contra la línia principal. TBD pren aquella pràctica antiga i la reivindica amb arguments moderns.

La seva formulació actual ve de dos llocs. Del moviment d'integració contínua —Kent Beck i la programació extrema als anys noranta, que ja deia «integra almenys una vegada al dia»— i del llibre Continuous Delivery de Jez Humble i David Farley (2010). És també el model que fan servir internament Google, Facebook i altres empreses amb repositoris enormes.

Els elements del model:

  • Un tronc (main, trunk, master): l'única branca de vida llarga.
  • Tothom hi integra almenys una vegada al dia.
  • Les branques, si existeixen, viuen hores, no dies.
  • La feina sense acabar viatja al tronc amagada darrere d'una bandera.
  • El tronc està sempre sa, garantit per proves automàtiques.

La diferència amb GitHub Flow és més petita del que sembla i més important del que sembla. Tots dos tenen una sola branca de vida llarga. La diferència és la freqüència obligatòria d'integració i les tècniques que calen per sostenir-la. GitHub Flow diu «branques curtes»; TBD diu «menys d'un dia, sense excepcions, i aquí tens com».

  1. Per què l'objectiu real és reduir el temps entre integracions

Aquest apartat és el fonament. Si no s'entén això, TBD sembla una regla arbitrària.

La corba del conflicte

Quan dues persones treballen sobre el mateix codi en branques separades, les seves versions divergeixen. I el cost de reconciliar aquella divergència no creix de manera lineal amb el temps: creix molt més de pressa.

Temps separat Commits divergents Cost típic d'integrar
Hores 1–3 Gairebé sempre automàtic
1 dia 3–10 Conflicte ocasional, trivial
1 setmana 20–50 Uns quants conflictes, mitja hora
2 setmanes 50–150 Conflictes seriosos, mig dia, risc d'error
1 mes o més 150+ Dies, i sovint es refà la feina

La raó de l'acceleració és combinatòria. Amb dos commits divergents hi ha poques maneres de xocar. Amb dos-cents, la probabilitat que algú hagi tocat el mateix fitxer és gairebé u, i a més els canvis s'han construït sobre supòsits diferents: no és només que les línies xoquin, és que la funció que anaves a modificar ja no existeix, o el mòdul s'ha dividit en tres.

I hi ha un tipus de conflicte que Git no detecta: el conflicte semàntic. L'Ana reanomena una funció a app.js i la Carla, a la seva branca, hi afegeix una crida a aquella funció amb el nom antic. En fusionar, no hi ha conflicte textual —toquen línies diferents— però el codi està trencat. Com més temps separades, més n'apareixen, i només els detecten les proves.

flowchart LR
    A["Integració<br/>poc freqüent"] --> B["Divergència<br/>gran"]
    B --> C["Conflictes costosos<br/>i arriscats"]
    C --> D["Por d'integrar"]
    D --> A
    E["Integració<br/>diària"] --> F["Divergència<br/>mínima"]
    F --> G["Conflictes trivials<br/>o inexistents"]
    G --> H["Integrar és rutina"]
    H --> E

Els dos bucles són estables. El de dalt és el que pateix l'equip de gestor-tasques dues vegades al mes. El de baix és on vol arribar l'Ana.

La conseqüència: és un problema de freqüència, no d'eina

Aquí hi ha el gir conceptual que dona nom a la pràctica. La integració contínua no és un servidor de CI: és integrar de debò, i sovint. Un equip pot tenir el millor servidor de comprovacions del món i no estar fent integració contínua, perquè cada persona treballa tres setmanes a la seva branca i el servidor només prova branques aïllades. Tornarem sobre aquesta idea a la lliçó 07-06, perquè és la definició mateixa de CI.

TBD és simplement la política de ramificació que fa possible la integració contínua de debò. Tota la resta del model són tècniques perquè aquella política sigui viable.

  1. Les dues variants del model

TBD admet dues formes, i l'elecció depèn sobretot de la mida de l'equip i de si la revisió és obligatòria.

Variant A: commit directe al tronc

Cada persona confirma directament a main, diverses vegades al dia.

git switch main
git pull --rebase
# editar app.js
git commit -am "Afegeix l'esquelet del panell d'estadistiques"
git pull --rebase
git push

El pull --rebase abans d'enviar manté l'historial lineal i evita commits de fusió trivials (lliçó 05-01).

Requisits innegociables: bateria de proves ràpida que s'executi abans de confirmar, equip petit (2–5 persones), molta confiança mútua i, normalment, programació en parella com a substitut de la revisió asíncrona.

Quan té sentit: equips molt petits i experimentats, o projectes on el cost d'un error és baix. A la pràctica és minoritària, perquè xoca frontalment amb la revisió obligatòria i amb les branques protegides de la lliçó 07-04.

Variant B: branques de vida molt curta

La variant majoritària. Branques que viuen hores, amb pull request i revisió ràpida.

# 09:15 — comenca la feina
git switch main && git pull
git switch -c ana/panel-estadistiques

# 09:15–11:30 — treballar
git commit -am "Afegeix l'esquelet del panell d'estadistiques"
git push -u origin ana/panel-estadistiques

# 11:30 — PR, revisio en menys d'una hora, integrar, esborrar branca
# 12:45 — de tornada al tronc. Comenca la branca seguent.

S'assembla molt a GitHub Flow, i de fet la frontera entre tots dos és difusa. Les diferències reals:

GitHub Flow TBD variant B
Vida de la branca Dies (de vegades més) Hores, menys d'un dia
Abast de la branca Una funcionalitat completa Un pas cap a la funcionalitat
S'integra codi sense acabar? No , darrere d'una bandera
Revisió Acurada, pot trigar Ràpida, minimitzant l'espera

La segona fila és la clau. A GitHub Flow, la unitat de treball és «la funcionalitat». A TBD, la unitat és «el que puc integrar avui sense trencar res». Una funcionalitat gran es converteix en vuit o deu integracions successives, cadascuna segura per si mateixa.

Un apunt sobre noms: a TBD és habitual prefixar la branca amb el nom de qui la crea (ana/panel-estadistiques, bruno/cache-etiquetes). Deixa clar d'un cop d'ull que és una branca personal i efímera, no una branca compartida del projecte.

  1. Separar desplegament de publicació: banderes de funcionalitat

I ara, l'objecció del principi: com s'integra diàriament codi sense acabar en una branca que es desplega a producció?

La resposta és una de les idees més importants de l'enginyeria de programari moderna:

Desplegar (que el codi sigui en producció) i publicar (que els usuaris el facin servir) són dues coses diferents i poden passar en moments diferents.

El mecanisme són les banderes de funcionalitat (feature flags, feature toggles): condicions que decideixen en temps d'execució si una funcionalitat està activa.

L'exemple a gestor-tasques

L'Ana està construint el panell d'estadístiques. Li portarà dues setmanes. En comptes d'una branca de dues setmanes, el primer dia integra això a app.js:

// configuracio/banderes.js
// Banderes de funcionalitat de gestor-tasques.
// Cada entrada documenta qui la va crear i quan s'ha de retirar.
export const BANDERES = {
  // GT-352 — Ana Ferrer — creada 2026-08-03 — retirar despres de la publicacio
  panelEstadistiques: false,

  // GT-338 — Bruno Salas — creada 2026-07-20 — retirar abans del 2026-08-15
  exportacioCSV: true,
};
// app.js
import { BANDERES } from './configuracio/banderes.js';

function renderitzaBarraLateral() {
  const barra = document.querySelector('#barra-lateral');
  barra.innerHTML = '';
  barra.append(construeixLlistaDeFiltres());

  // La funcionalitat viatja a produccio, pero apagada.
  if (BANDERES.panelEstadistiques) {
    barra.append(construeixPanelEstadistiques());
  }
}

Què ha aconseguit amb això:

  • El codi és en producció des del primer dia. Es desplega, es compila, es prova al costat de la resta.
  • Cap usuari no el veu, perquè la bandera està apagada.
  • No hi ha branca de dues setmanes, així que no hi ha divergència ni conflictes grans.
  • Cada dia l'Ana integra un pas més de construeixPanelEstadistiques(), sempre darrere de la mateixa bandera.
  • El dia que està llesta, publicar-la és canviar false per true: un canvi d'una línia, sense desplegar codi nou.
  • I si alguna cosa va malament, apagar-la és canviar true per false: la reversió més ràpida i menys arriscada que hi ha. Res de git revert amb pressa a les onze de la nit.

Aquest últim punt se subestima. Revertir un desplegament amb Git significa construir, provar i desplegar de nou; apagar una bandera és immediat i no toca el codi.

Graus de sofisticació

Les banderes admeten molta més lògica que un booleà en un fitxer:

// banderes.js — versio amb activacio selectiva
const CONFIGURACIO = {
  panelEstadistiques: {
    activa: true,
    // Nomes per a l'equip intern mentre s'afina
    usuaris: ['[email protected]', '[email protected]'],
    // I per a un 10 % de la resta, per mesurar l'impacte
    percentatge: 10,
  },
};

export function estaActiva(nom, usuari) {
  const b = CONFIGURACIO[nom];
  if (!b || !b.activa) return false;
  if (b.usuaris?.includes(usuari.email)) return true;
  if (b.percentatge) return hashEstable(usuari.id) % 100 < b.percentatge;
  return true;
}
// Us a app.js
if (estaActiva('panelEstadistiques', usuariActual)) {
  barra.append(construeixPanelEstadistiques());
}

Amb això apareixen possibilitats que no tenen res a veure amb Git però que expliquen per què les banderes s'han generalitzat tant: desplegament progressiu (activar per a l'1 %, després el 10 %, després tothom), proves A/B, activació per client i desactivació d'emergència.

Les quatre famílies de bandera

No totes les banderes són iguals, i confondre-les és l'origen de la majoria dels problemes:

Tipus Per a què Vida esperada Es retira?
De publicació Amagar feina en curs fins que estigui llesta Dies o setmanes Sí, sempre
D'experiment Prova A/B, mesurar una variant Setmanes , en decidir
Operativa Apagar alguna cosa pesada sota càrrega (kill switch) Permanent No
De permisos Funcionalitats per pla de subscripció Permanent No

Les dues primeres són temporals per definició, i tractar-les com si fossin permanents és exactament el problema de l'apartat següent. Les dues últimes són configuració legítima del producte i no són deute.

Un detall important de Git: el fitxer de banderes és codi versionat. Canviar una bandera és un commit, amb el seu missatge, la seva revisió i el seu rastre a l'historial. Quan algú pregunti «quan es va activar el panell d'estadístiques?», git log -- configuracio/banderes.js ho respon. Les plataformes de gestió de banderes mouen aquella configuració fora del repositori, cosa que guanya immediatesa i perd traçabilitat; és un intercanvi conscient.

  1. El cost de les banderes: el deute de banderes

Les banderes no són gratis, i convé dir-ho amb la mateixa claredat amb què se'n venen els avantatges.

Cada bandera duplica els camins possibles del codi. Amb una bandera hi ha dos comportaments per provar. Amb deu banderes independents hi ha, en teoria, 1024 combinacions. Ningú no prova 1024 combinacions. A la pràctica se'n proven dues o tres, i la resta són territori inexplorat on viuen les fallades rares que només li passen a un client.

Els símptomes d'un equip amb deute de banderes:

  • Banderes de fa dos anys que ningú no gosa tocar perquè ningú no sap què fan.
  • Codi mort darrere de banderes permanentment apagades, que se segueix compilant i mantenint.
  • Condicionals imbricats de banderes: if (A && !B) { ... } else if (B && C) { ... }.
  • Fallades que només es reprodueixen amb una combinació concreta de banderes, impossibles de depurar.
  • Ningú no sap què està realment actiu en producció.

La disciplina que ho evita

Regla 1: tota bandera temporal neix amb data de caducitat. Escrita al codi mateix, com a l'exemple de l'apartat anterior. Si en arribar la data encara hi és, és un tiquet automàtic.

Regla 2: retirar la bandera forma part de la tasca, no és un extra. La funcionalitat no està «acabada» quan es publica: està acabada quan la bandera ha desaparegut del codi. Molts equips creen el tiquet de retirada en el mateix moment de crear la bandera.

Regla 3: la retirada és un canvi propi i petit.

// Abans
if (BANDERES.panelEstadistiques) {
  barra.append(construeixPanelEstadistiques());
}

// Despres: s'elimina la condicio i l'entrada a banderes.js
barra.append(construeixPanelEstadistiques());
git switch -c neteja/retirar-bandera-panel-estadistiques
git commit -am "Retira la bandera panelEstadistiques

La funcionalitat porta tres setmanes activa al 100 % sense incidencies.
Tanca GT-352."

Regla 4: audita periòdicament. Una ordre senzilla, executable des de la CI, que llisti les banderes i la seva antiguitat:

# Banderes declarades i quan es va tocar cada linia per ultima vegada
git blame --date=short -- configuracio/banderes.js | grep -E '^\S+.*: (true|false)'

Regla 5: posa-hi un sostre. Alguns equips limiten el nombre de banderes temporals simultànies (per exemple, cinc). Per crear la sisena, cal retirar-ne una. És artificial, i funciona.

L'intercanvi, dit sense adorns: les banderes canvien complexitat de ramificació (que Git gestiona malament en branques llargues) per complexitat de codi (que es gestiona amb disciplina i esborrat). És un bon negoci només si l'esborrat passa de debò. Un equip que afegeix banderes i no les retira acaba en un lloc pitjor que si hagués fet servir branques llargues.

  1. Branch by abstraction: canvis grans sense branques llargues

Les banderes resolen bé «afegir una cosa nova, amagada». No resolen bé substituir una cosa existent per una altra: reemplaçar l'emmagatzematge a localStorage per una API al servidor, migrar components-ui a una versió incompatible, reescriure el motor de renderitzat.

Per a això existeix branch by abstraction («ramificar per abstracció»), una tècnica que aconsegueix el mateix efecte sense branques i sense banderes escampades per tot el codi. Són cinc passos, i cadascun s'integra al tronc per separat.

Pas 1: introduir una abstracció entre el codi que crida i la implementació actual.

// emmagatzematge/index.js  — nova capa, sense canviar el comportament
import * as local from './emmagatzematge-local.js';

export const desaTasques   = local.desaTasques;
export const carregaTasques = local.carregaTasques;

Tot el codi passa a fer servir emmagatzematge/index.js. No canvia res funcionalment, així que és una integració segura i revisable en deu minuts.

Pas 2: construir la nova implementació darrere de la mateixa interfície. S'integra diàriament, sense que ningú la faci servir encara.

// emmagatzematge/emmagatzematge-api.js
export async function desaTasques(tasques) { /* crida al servidor */ }
export async function carregaTasques() { /* crida al servidor */ }

Pas 3: triar la implementació amb una bandera. Una de sola, en un sol lloc.

// emmagatzematge/index.js
import { BANDERES } from '../configuracio/banderes.js';
import * as local from './emmagatzematge-local.js';
import * as api   from './emmagatzematge-api.js';

const impl = BANDERES.emmagatzematgeEnServidor ? api : local;

export const desaTasques    = impl.desaTasques;
export const carregaTasques = impl.carregaTasques;

Pas 4: migrar progressivament. Activar per a l'equip, després per al 5 %, després per a tothom, vigilant.

Pas 5: retirar el que és vell. Esborrar emmagatzematge-local.js, esborrar la bandera i —si ja no aporta res— aplanar l'abstracció.

L'avantatge davant d'una branca de dos mesos és contundent: en cap moment no hi ha una divergència gran. Cada pas és un canvi petit, revisable, integrat i desplegat. I si a mig camí cal parar i atendre una altra prioritat, la feina feta ja és al tronc i no es perd ni es floreix.

  1. Branques de versió només quan fan falta

TBD no prohibeix les branques de versió: diu que no han d'existir per defecte. Es creen quan hi ha una raó concreta, i sempre des del tronc.

# Nomes si cal congelar una versio concreta
git switch -c release/3.2 main
git tag -a v3.2.0 -m "Versio 3.2.0"
git push -u origin release/3.2 --follow-tags

I hi ha una regla direccional que defineix el model:

Les correccions es fan primer al tronc i després es porten a la branca de versió, mai a l'inrevés.

# 1. Arreglar al tronc (on s'arregla TOT, sempre)
git switch main && git pull
git switch -c correccio/data-venciment
git commit -am "Corregeix la zona horaria de la data de venciment"
# PR, revisio, integrar a main

# 2. Portar l'arranjament a la versio afectada
git switch release/3.2
git cherry-pick <hash-de-l-arranjament-a-main>
git tag -a v3.2.1 -m "Versio 3.2.1"
git push origin release/3.2 --follow-tags

Aquesta tècnica s'anomena cherry-pick cap enrere (backporting), i fa servir exactament l'ordre de la lliçó 05-03. Compara-la amb el hotfix/* de Git Flow, que arreglava primer a main (producció) i després propagava a develop:

Git Flow TBD
On s'arregla primer A la branca de producció (hotfix/ des de main) Sempre al tronc
Com arriba a l'altre lloc Fusió de tornada a develop cherry-pick a la branca de versió
Risc d'oblit L'arranjament no arriba a desenvolupament: la fallada torna L'arranjament no arriba a la versió antiga: el client continua amb la fallada

El risc de TBD és més benigne. Si t'oblides del cherry-pick, el client antic continua amb la fallada —dolent, però visible i reclamable—. Si a Git Flow t'oblides de la segona fusió, la fallada reapareix a la versió següent, que és pitjor i molt més confús.

Les branques de versió a TBD han de viure poc, a més: es creen, reben els seus pedaços, i quan la versió deixa de mantenir-se s'abandonen. No són branques permanents.

  1. Què exigeix TBD

Com GitHub Flow, TBD compra simplicitat estructural pagant en disciplina i automatització. Aquí la factura és encara més alta.

Exigència 1: bateria de proves ràpida i fiable

Ràpida és tan important com fiable. Si la CI triga quaranta minuts, ningú no integra tres vegades al dia: la gent acumula feina per amortitzar l'espera, i ja no estàs fent TBD. La referència pràctica és menys de deu minuts per a la bateria que bloqueja la integració. Com aconseguir-ho —memòria cau, paral·lelisme, executar només l'afectat— és tema de la lliçó 07-06.

Fiable significa zero proves inestables. Amb integracions diàries de tot l'equip, una prova que falla el 5 % de les vegades es converteix en diversos falsos vermells al dia i l'equip aprèn a ignorar-los.

Exigència 2: cultura de commits petits

Cal saber descompondre la feina. És una habilitat concreta i s'entrena: davant de «construir el panell d'estadístiques», saber-hi veure vuit passos que es poden integrar per separat sense trencar res. Qui només sap pensar en unitats de «funcionalitat completa» no pot fer TBD.

Exigència 3: CI a cada enviament

Cada enviament a qualsevol branca, i cada integració al tronc, dispara les comprovacions. Sense excepcions.

Exigència 4: el tronc sa és la prioritat absoluta

Igual que a GitHub Flow, però més agut: aquí tot l'equip treballa contra el tronc diverses vegades al dia. Un tronc trencat no bloqueja qui desplega, bloqueja tothom alhora. La política habitual és revertir immediatament i arreglar en una branca.

Exigència 5: revisió ràpida

Si una PR de tres hores de feina espera dos dies a ser revisada, el model es trenca. Els equips que fan TBD adopten compromisos explícits (revisar en menys d'una hora), revisió en parella, o programació en parella com a substitut.

Exigència 6: maduresa per fer servir bé les banderes

Crear banderes és fàcil. Retirar-les exigeix disciplina sostinguda. Un equip que no la tingui acumularà deute de banderes fins que el codi sigui inmanejable.

  1. Comparativa final dels tres fluxos

Ja tenim els tres models complets. Aquesta és la taula que resumeix el mòdul.

Criteri Git Flow GitHub Flow Trunk Based
Branques de vida llarga 2 (main, develop) + suport 1 (main) 1 (tronc)
Classes de branca de suport 3 (feature, release, hotfix) 1 (branca de treball) 1 (branca efímera) o cap
Freqüència d'integració Baixa: en acabar la funcionalitat (setmanes) Mitjana: dies Alta: almenys una vegada al dia
Vida típica d'una branca 1–4 setmanes 2–5 dies Hores
Complexitat del procés Alta: 5 classes, doble integració Baixa Molt baixa en branques, alta en tècnica
Tipus de lliurament Planificat, per versions Continu, després de cada PR Continu, diversos al dia
Mida d'equip Mitjà i gran, amb rols Petit i mitjà Petit a molt gran (amb inversió)
Mantenir versions antigues Excel·lent: és la seva raó de ser Dolent: requereix afegir branques de suport Acceptable: branques de versió + backport
Cost dels conflictes Alt: divergència llarga Mitjà Molt baix: gairebé no hi ha divergència
Exigència de proves automàtiques Mitjana (hi ha QA manual a la branca de versió) Alta Molt alta
Temps d'idea a producció Setmanes o mesos Dies Hores
Corba d'aprenentatge Alta (molt protocol) Baixa Mitjana (el protocol és simple; les tècniques no)
Risc per desplegament Alt: molts canvis junts Baix: un canvi per desplegament Molt baix: canvis diminuts
Feina gran sense acabar A la seva branca, aïllada Problema no resolt Banderes i branch by abstraction
Reversió d'emergència Nou hotfix + versió de pedaç git revert + desplegament Apagar una bandera: immediat

Dues lectures que convé extreure de la taula:

Primera: la complexitat no desapareix, es canvia de lloc. Git Flow la posa a l'estructura de branques (cinc classes, regles d'origen i destinació, dobles integracions). TBD la posa al codi i l'automatització (banderes, abstraccions, proves ràpides, disciplina). GitHub Flow queda al mig. Cap model no és «més simple» en termes absoluts: trien on pagar.

Segona: hi ha una relació directa entre maduresa tècnica i model viable. Com menys fiable és la teva automatització, més estructura de branques necessites com a xarxa de seguretat. Un equip sense proves automàtiques que adopta TBD no està sent modern: està desplegant codi sense comprovar. La progressió natural és Git Flow → GitHub Flow → TBD a mesura que millora l'automatització, no a l'inrevés.

  1. Arbre de decisió: quin triar

flowchart TD
    A["Mantens diverses versions<br/>en producció alhora?"]
    A -->|Sí| B["Lliuraments planificats<br/>amb fase de QA manual?"]
    A -->|No| C["Tens proves automàtiques<br/>fiables i CI?"]
    B -->|Sí| D["**Git Flow**<br/>Producte instal·lable, mòbil,<br/>biblioteques, encastats"]
    B -->|No| E["**GitHub Flow + branques<br/>de suport**<br/>des de les etiquetes"]
    C -->|No| F["**GitHub Flow**<br/>i prioritza construir<br/>la bateria de proves"]
    C -->|Sí| G["La CI triga menys<br/>de 10 minuts?"]
    G -->|No| H["**GitHub Flow**<br/>i treballa a accelerar la CI<br/>abans de fer el pas"]
    G -->|Sí| I["L'equip sap descompondre<br/>i retirar banderes?"]
    I -->|No| J["**GitHub Flow**<br/>amb branques cada cop més curtes<br/>com a transició"]
    I -->|Sí| K["**Trunk Based**<br/>Web, SaaS, desplegament<br/>continu, equips madurs"]

I quatre criteris pràctics que valen més que qualsevol diagrama:

1. Tria el model més simple que resolgui el teu problema real. No adoptis branques de versió «per si algun dia fan falta». Afegeix la peça el dia que la necessitis, com va fer l'equip de gestor-tasques amb suport/1.4.

2. Els models es barregen. Gairebé cap equip no aplica un d'aquests tres en la seva forma canònica. GitHub Flow amb una branca de suport, TBD amb branques de versió trimestrals, Git Flow sense develop. El que importa és que tot l'equip sàpiga quin és l'acord, no que tingui nom propi.

3. La restricció real gairebé mai no és Git. És quant triguen les proves, quant es triga a revisar i quant costa desplegar. Si vols integrar més sovint i no pots, el coll d'ampolla és en un d'aquests tres llocs, no a la política de branques.

4. Escriu-ho. L'acord ha de ser al CONTRIBUTING.md del repositori: quines branques existeixen, d'on neixen, com s'integren, quin mètode d'integració es fa servir i què cal complir per integrar. Un model que només viu al cap de tres persones no sobreviu a la quarta contractació.

Errors Habituals i Consells

Error 1: adoptar TBD sense proves automàtiques. No és TBD, és enviar codi sense comprovar directament a producció. Construeix primer la xarxa.

Error 2: anomenar TBD a branques d'una setmana. Si la branca viu dies, és GitHub Flow. Està bé, però no és això, i no obtindràs la reducció de conflictes.

Error 3: crear banderes i no retirar-les mai. El deute de banderes fa el codi inmanejable i acaba sent pitjor que les branques llargues que evitava.

Error 4: banderes imbricades. if (A && !B) ... else if (B && C) ... és impossible de provar i de raonar. Una bandera, un punt de decisió.

Error 5: fer servir banderes per substituir una implementació per una altra en vint llocs. Per a això hi ha branch by abstraction: una sola bandera en un sol punt.

Error 6: no canviar la manera de descompondre la feina. TBD amb la mentalitat d'«acabo la funcionalitat i després integro» no funciona. Cal aprendre a partir-la en passos integrables.

Error 7: conviure amb una CI lenta. És la causa més comuna del fracàs de TBD. Si la CI triga quaranta minuts, el model és inviable per moltes ganes que s'hi posin.

Error 8: arreglar directament a la branca de versió. L'arranjament es perd per al tronc i reapareix a la versió següent. S'arregla al tronc i s'hi porta amb cherry-pick.

Error 9: triar model per moda. TBD perquè el fa servir Google, quan el teu equip són dues persones sense proves automàtiques. Google també té mil enginyers dedicats a la infraestructura que ho fa possible.

Consell 1: mesura el temps de vida de les teves branques. git for-each-ref --sort=committerdate --format='%(committerdate:short) %(refname:short)' refs/remotes/origin/ et dona la foto en una línia. Si la mitjana és de dues setmanes, ja saps per on començar.

Consell 2: documenta cada bandera amb tiquet, autor i data de caducitat. Al fitxer mateix, al costat de la declaració.

Consell 3: crea el tiquet de retirada en crear la bandera. No en publicar: en crear-la.

Consell 4: transició gradual. Passar de branques de dues setmanes a integració diària de cop no funciona. Redueix a una setmana, després a tres dies, després a un, arreglant a cada pas el que es trenqui.

Consell 5: git pull --rebase com a costum. Amb integracions freqüents evita desenes de commits de fusió inútils. Configura'l amb git config --global pull.rebase true (lliçó 01-06).

Consell 6: escriu l'acord al CONTRIBUTING.md. Amb exemples d'ordres concretes, no només amb la teoria.

Exercicis

Exercici 1: el cost real de la divergència

Demostra empíricament per què integrar sovint redueix els conflictes.

  1. Crea un repositori amb app.js de vint línies numerades.
  2. Escenari A (divergència gran): crea dues branques i fes sis commits a cadascuna, tocant línies properes del mateix fitxer. Fusiona-les i compta els conflictes.
  3. Escenari B (integració freqüent): parteix del mateix estat inicial en una còpia. Alterna: un commit a la branca 1, integrar a main; un commit a la branca 2 rebasada sobre main, integrar; i així fins a sis per a cadascuna.
  4. Compara el nombre de conflictes i de línies en conflicte entre els dos escenaris.
  5. Provoca un conflicte semàntic: en una branca reanomena una funció i a l'altra afegeix una crida amb el nom antic. Comprova que Git fusiona sense queixar-se i que el resultat està trencat.

Exercici 2: banderes de funcionalitat

  1. Crea configuracio/banderes.js amb dues banderes documentades (tiquet, autor, data de caducitat).
  2. Implementa a app.js una funcionalitat nova amagada darrere d'una bandera apagada, en tres commits integrats a main un a un (esquelet, lògica, presentació). Comprova que a cada pas l'aplicació continua sent funcional amb la bandera apagada.
  3. «Publica» la funcionalitat amb un commit que només canviï false per true.
  4. Simula una incidència: reverteix la publicació amb un commit que torni a posar false, i compara-ho amb el que hauria costat revertir el codi sencer.
  5. Retira la bandera: elimina la condició i l'entrada del fitxer, en un commit propi amb un missatge que expliqui per què.
  6. Escriu una ordre que llisti les banderes del fitxer al costat de la data de l'últim canvi de cada línia.

Exercici 3: branch by abstraction

Migra l'emmagatzematge de gestor-tasques de localStorage a una API simulada, sense branques llargues. Cada pas ha de ser un commit a main que deixi l'aplicació funcionant.

  1. Estat inicial: app.js crida directament a localStorage.
  2. Pas 1: crea emmagatzematge/index.js que reexporti la implementació actual, i fes que app.js la faci servir. Sense canvi de comportament.
  3. Pas 2: afegeix emmagatzematge/emmagatzematge-api.js amb la nova implementació (pot ser simulada).
  4. Pas 3: selecciona la implementació amb una bandera en un únic punt.
  5. Pas 4: activa la bandera.
  6. Pas 5: elimina la implementació antiga i la bandera.
  7. Mostra git log --oneline i comprova que són sis passos petits i independents, cadascun desplegable.

Solucions

Solució 1:

mkdir /tmp/tbd-conflictes && cd /tmp/tbd-conflictes
git init -qb main
seq 1 20 | sed 's/^/const linia/;s/$/ = 0;/' > app.js
git add . && git commit -q -m "Estat inicial"
cd /tmp && cp -r tbd-conflictes tbd-frequent && cd /tmp/tbd-conflictes
# Escenari A: divergencia gran
git switch -qc branca-ana
for i in 1 2 3 4 5 6; do
  sed -i "${i}s/= 0;/= ${i}00;  \/\/ ana/" app.js
  git commit -qam "Ana canvi $i"
done
git switch -q main
git switch -qc branca-bruno
for i in 1 2 3 4 5 6; do
  sed -i "${i}s/= 0;/= ${i}99;  \/\/ bruno/" app.js
  git commit -qam "Bruno canvi $i"
done
git switch -q main
git merge -q branca-ana
git merge branca-bruno
Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
Automatic merge failed; fix conflicts and then commit the result.
grep -c '^<<<<<<<' app.js      # nombre de blocs en conflicte
git merge --abort
# Escenari B: integracio frequent
cd /tmp/tbd-frequent
for i in 1 2 3 4 5 6; do
  git switch -q main
  git switch -qc ana-$i
  sed -i "${i}s/= 0;/= ${i}00;  \/\/ ana/" app.js
  git commit -qam "Ana canvi $i"
  git switch -q main && git merge -q ana-$i && git branch -qd ana-$i

  git switch -qc bruno-$i
  sed -i "${i}s/\/\/ ana/\/\/ ana bruno/" app.js
  git commit -qam "Bruno canvi $i"
  git switch -q main && git merge -q bruno-$i && git branch -qd bruno-$i
done
echo "Integracions completades sense conflicte"

A l'escenari B cada branca parteix del tronc actualitzat i la divergència no supera mai un commit: no hi ha ni un conflicte. La mateixa feina, el mateix fitxer, cost diferent.

# 5. Conflicte semantic
cd /tmp/tbd-conflictes
git switch -q main
printf 'function desaTasques(t) { return t; }\ndesaTasques([]);\n' > tasques.js
git add . && git commit -q -m "Afegeix tasques.js"
git switch -qc reanomenat
sed -i 's/function desaTasques/function persisteixTasques/;s/^desaTasques(\[\]);/persisteixTasques([]);/' tasques.js
git commit -qam "Reanomena desaTasques a persisteixTasques"
git switch -q main
git switch -qc nova-crida
echo 'desaTasques([{id: 1}]);' >> tasques.js
git commit -qam "Afegeix una crida mes"
git switch -q main
git merge -q reanomenat
git merge nova-crida
cat tasques.js
function persisteixTasques(t) { return t; }
persisteixTasques([]);
desaTasques([{id: 1}]);

Git fusiona sense conflicte perquè les línies són diferents, i el resultat crida una funció que ja no existeix. Només ho detecten les proves, i només si existeixen.

Solució 2:

mkdir /tmp/tbd-banderes && cd /tmp/tbd-banderes
git init -qb main
mkdir configuracio
cat > configuracio/banderes.js <<'EOF'
// Banderes de funcionalitat de gestor-tasques.
export const BANDERES = {
  // GT-352 — Ana Ferrer — creada 2026-08-03 — retirar abans del 2026-09-01
  panelEstadistiques: false,

  // GT-338 — Bruno Salas — creada 2026-07-20 — retirar abans del 2026-08-15
  exportacioCSV: true,
};
EOF
printf "import { BANDERES } from './configuracio/banderes.js';\n\nfunction renderitzaBarraLateral() {\n  const barra = document.querySelector('#barra-lateral');\n  barra.innerHTML = '';\n}\n" > app.js
git add . && git commit -q -m "Afegeix el fitxer de banderes de funcionalitat"
# 2. Tres passos, tres integracions, sempre desplegable
cat >> app.js <<'EOF'

function construeixPanelEstadistiques() {
  const panel = document.createElement('section');
  panel.className = 'panel-estadistiques';
  return panel;
}
EOF
git commit -qam "Afegeix l'esquelet del panell d'estadistiques (GT-352)"

sed -i "s|  panel.className = 'panel-estadistiques';|  panel.className = 'panel-estadistiques';\n  panel.dataset.total = String(calculaTotals().total);|" app.js
cat >> app.js <<'EOF'

function calculaTotals() {
  return { total: 0, completades: 0 };
}
EOF
git commit -qam "Calcula els totals del panell d'estadistiques (GT-352)"

sed -i "s|  barra.innerHTML = '';|  barra.innerHTML = '';\n  if (BANDERES.panelEstadistiques) {\n    barra.append(construeixPanelEstadistiques());\n  }|" app.js
git commit -qam "Mostra el panell d'estadistiques darrere la seva bandera (GT-352)"
# 3. Publicar: una linia
sed -i 's/panelEstadistiques: false,/panelEstadistiques: true,/' configuracio/banderes.js
git commit -qam "Activa el panell d'estadistiques per a tots els usuaris

Tanca GT-352 (publicacio)."
# 4. Reversio d'emergencia: una altra linia
sed -i 's/panelEstadistiques: true,/panelEstadistiques: false,/' configuracio/banderes.js
git commit -qam "Desactiva el panell d'estadistiques: error de calcul als totals

Incidencia GT-360."

Revertir el codi sencer hauria exigit git revert de tres commits, resoldre els conflictes amb el que s'hi ha integrat després, construir i desplegar. Apagar la bandera és un canvi d'una línia que no toca la lògica.

# 5. Retirada (despres de reactivar i estabilitzar)
sed -i 's/panelEstadistiques: false,/panelEstadistiques: true,/' configuracio/banderes.js
git commit -qam "Reactiva el panell d'estadistiques despres de corregir els totals"

python3 - <<'EOF'
import re
s = open('app.js').read()
s = s.replace("""  if (BANDERES.panelEstadistiques) {
    barra.append(construeixPanelEstadistiques());
  }""", "  barra.append(construeixPanelEstadistiques());")
open('app.js','w').write(s)
EOF
sed -i '/GT-352/,+1d' configuracio/banderes.js
git commit -qam "Retira la bandera panelEstadistiques

Porta tres setmanes activa al 100 % sense incidencies. La condicio ja no
aporta res i elimina un cami de codi per provar. Tanca GT-352."
# 6. Auditoria de banderes
git blame --date=short -- configuracio/banderes.js | grep -E ': (true|false),'

Solució 3:

mkdir /tmp/tbd-abstraccio && cd /tmp/tbd-abstraccio
git init -qb main
mkdir -p emmagatzematge configuracio
printf "function desa(t) { localStorage.setItem('tasques', JSON.stringify(t)); }\nfunction carrega() { return JSON.parse(localStorage.getItem('tasques') || '[]'); }\n" > app.js
echo "export const BANDERES = {};" > configuracio/banderes.js
git add . && git commit -q -m "Estat inicial: emmagatzematge a localStorage"
# Pas 1: l'abstraccio, sense canviar el comportament
cat > emmagatzematge/emmagatzematge-local.js <<'EOF'
export function desaTasques(t) { localStorage.setItem('tasques', JSON.stringify(t)); }
export function carregaTasques() { return JSON.parse(localStorage.getItem('tasques') || '[]'); }
EOF
cat > emmagatzematge/index.js <<'EOF'
import * as local from './emmagatzematge-local.js';
export const desaTasques    = local.desaTasques;
export const carregaTasques = local.carregaTasques;
EOF
printf "import { desaTasques, carregaTasques } from './emmagatzematge/index.js';\n\nexport { desaTasques, carregaTasques };\n" > app.js
git add . && git commit -q -m "Introdueix la capa d'abstraccio d'emmagatzematge

Sense canvi de comportament: index.js reexporta la implementacio
actual basada en localStorage. Prepara la migracio a servidor."
# Pas 2: la nova implementacio, sense fer-la servir
cat > emmagatzematge/emmagatzematge-api.js <<'EOF'
export async function desaTasques(t) {
  await fetch('/api/tasques', { method: 'PUT', body: JSON.stringify(t) });
}
export async function carregaTasques() {
  const r = await fetch('/api/tasques');
  return r.ok ? r.json() : [];
}
EOF
git add . && git commit -q -m "Afegeix la implementacio d'emmagatzematge contra l'API

Encara no la fa servir ningu: index.js continua apuntant a localStorage."
# Pas 3: la bandera, en un unic punt
cat > configuracio/banderes.js <<'EOF'
export const BANDERES = {
  // GT-377 — Ana Ferrer — creada 2026-08-05 — retirar despres de la migracio
  emmagatzematgeEnServidor: false,
};
EOF
cat > emmagatzematge/index.js <<'EOF'
import { BANDERES } from '../configuracio/banderes.js';
import * as local from './emmagatzematge-local.js';
import * as api   from './emmagatzematge-api.js';

const impl = BANDERES.emmagatzematgeEnServidor ? api : local;

export const desaTasques    = impl.desaTasques;
export const carregaTasques = impl.carregaTasques;
EOF
git add . && git commit -q -m "Selecciona la implementacio d'emmagatzematge amb una bandera (GT-377)"
# Pas 4: activar
sed -i 's/emmagatzematgeEnServidor: false,/emmagatzematgeEnServidor: true,/' configuracio/banderes.js
git commit -qam "Activa l'emmagatzematge en servidor per a tots els usuaris (GT-377)"

# Pas 5: retirar el que es vell
git rm -q emmagatzematge/emmagatzematge-local.js
cat > emmagatzematge/index.js <<'EOF'
export { desaTasques, carregaTasques } from './emmagatzematge-api.js';
EOF
echo "export const BANDERES = {};" > configuracio/banderes.js
git add . && git commit -q -m "Retira l'emmagatzematge a localStorage i la seva bandera

La migracio porta dues setmanes activa al 100 %. Tanca GT-377."
# 7. Sis passos, tots desplegables
git log --oneline
f2a9c1e Retira l'emmagatzematge a localStorage i la seva bandera
8d3b7f4 Activa l'emmagatzematge en servidor per a tots els usuaris (GT-377)
1c6e0a9 Selecciona la implementacio d'emmagatzematge amb una bandera (GT-377)
5b9d2f7 Afegeix la implementacio d'emmagatzematge contra l'API
3e7a4c8 Introdueix la capa d'abstraccio d'emmagatzematge
9f1c5b2 Estat inicial: emmagatzematge a localStorage

Una migració completa d'arquitectura, sense cap branca que hagi viscut més d'unes hores, i amb cada pas desplegable de manera independent.

Conclusió

Trunk Based Development és l'extrem de la integració freqüent, i el seu valor és entendre per què aquella freqüència importa. L'essencial:

  • La regla: tothom integra al tronc almenys una vegada al dia. Branques d'hores, o commit directe en equips molt petits.
  • L'objectiu real no és tenir poques branques: és reduir el temps entre integracions, perquè el cost d'un conflicte creix molt més ràpid que el temps de divergència. I els conflictes semàntics —els que Git no detecta— creixen igual.
  • La integració contínua no és un servidor de CI: és integrar de debò, i sovint. TBD és la política de branques que la fa possible.
  • La tècnica que ho permet és separar desplegament de publicació amb banderes de funcionalitat: el codi viatja a producció apagat, es publica canviant una línia i es reverteix apagant-la. Distingeix les banderes temporals (de publicació i experiment, cal retirar-les) de les permanents (operatives i de permisos).
  • Les banderes tenen un cost real: el deute de banderes multiplica els camins del codi. Data de caducitat documentada, tiquet de retirada creat en crear-les, retirada com a part de la tasca i auditoria periòdica.
  • Per substituir una implementació per una altra, branch by abstraction: abstracció, nova implementació, bandera en un únic punt, migració progressiva, retirada del que és vell. Cinc passos, cap branca llarga.
  • Les branques de versió existeixen només quan fan falta, i la direcció és sempre arreglar al tronc i portar l'arranjament amb cherry-pick, mai a l'inrevés.
  • Exigeix CI ràpida (menys de deu minuts) i fiable, cultura de commits petits, revisió ràpida, tronc sa com a prioritat absoluta i maduresa per gestionar banderes.
  • I la lectura de la comparativa: la complexitat no desapareix, es trasllada. Git Flow la posa a l'estructura de branques; TBD, al codi i l'automatització. Com menys fiable sigui la teva automatització, més estructura de branques necessites com a xarxa de seguretat.

Els tres models, amb totes les seves diferències, depenen del mateix: comprovacions automàtiques en les quals es pugui confiar. Git Flow les necessita menys perquè té QA manual; GitHub Flow les exigeix perquè main sigui desplegable; TBD les exigeix ràpides i perfectes perquè el tronc rep canvis cada hora. Sense elles, cap flux no funciona.

Toca per fi construir aquesta peça. A la lliçó 07-06: Integració Contínua amb Git veurem què és realment la integració contínua, com s'enganxa a Git mitjançant disparadors, quin commit es comprova exactament en una pull request —la resposta sorprèn—, com les comprovacions d'estat i les branques protegides es converteixen en un requisit que ningú no pot saltar-se amb un --no-verify, i reprendrem per fi els hooks de servidor que la lliçó 06-01 va deixar pendents.

Dominant Git: De Principiant a Avançat

Mòdul 1: Introducció a Git

Mòdul 2: Operacions Bàsiques de Git

Mòdul 3: Branques i Fusió

Mòdul 4: Treballant amb Repositoris Remots

Mòdul 5: Operacions Avançades de Git

Mòdul 6: Eines i Tècniques de Git

Mòdul 7: Estratègies de Col·laboració i Flux de Treball

Mòdul 8: Bones Pràctiques i Consells de Git

Mòdul 9: Resolució de Problemes i Depuració

Mòdul 10: Git al Món Real

© Copyright 2026. Tots els drets reservats