Arhitectura datelor și a sistemelor, de la zero Lecția 49 / 80

Git pentru echipele de inginerie: strategii de branching care funcționează

Trunk-based, GitHub flow, gitflow. Realitățile la scară mică vs scară mare, când se potrivește fiecare și pattern-urile care au supraviețuit 15 ani de practică.

Modulele 5 și 6 au acoperit jumătatea de data-platform a cursului: batch în Modulul 5, streaming în Modulul 6, două studii de caz (Netflix și Uber) care au exercitat fiecare primitivă pe parcurs. Modulul 7 deschide o discuție diferită. Designurile arhitecturale din modulele anterioare trebuie să ajungă cumva în producție, iar drumul de la „am scris designul” la „rulează” trece prin cod, version control, code review, teste automate, deployment pipelines și uneltele operaționale care țin luminile aprinse. Despre asta e Modulul 7.

Deschiderea alege piesa cea mai universală din toolchain: Git, și întrebarea la care orice echipă de inginerie răspunde cumva, care e cum folosește echipa branch-urile. Răspunsul contează mai mult decât pare. Strategia de branching modelează cadența de review, frecvența de deployment, frecvența conflictelor de merge și postura culturală a echipei față de livrare. O echipă care folosește gitflow livrează diferit față de o echipă care folosește trunk-based development, iar diferența nu e doar de proces: e experiența trăită de ingineri.

Lecția aceasta acoperă cele trei strategii care reprezintă aproape toată practica profesională. Gitflow, GitHub flow și trunk-based development. Lecția următoare se concentrează pe trunk-based development, pentru că e strategia spre care converg cele mai multe echipe moderne și cea care răsplătește cel mai mult o privire mai aprofundată. Apoi lecția 51 trece la CI pentru data pipelines, unde practicile abstracte de software engineering întâlnesc forma specifică a muncii cu date pe care o construiește restul cursului.

Cele trei strategii

Trei pattern-uri domină. Diferă prin durata de viață a branch-ului, complexitatea de merge, cadența de release și ce tooling au nevoie ca să funcționeze. Nu sunt singurele pattern-uri. Sunt cele pe care echipele chiar le folosesc și cele despre care e scris cel mai mult material public.

Gitflow

Postarea de blog din 2010 a lui Vincent Driessen, „A successful Git branching model”, a introdus gitflow (https://nvie.com/posts/a-successful-git-branching-model/, consultat 2026-05-01). Modelul este ierarhic. Două branch-uri cu durată lungă de viață ancorează repo-ul: main (uneori numit master în convențiile mai vechi) ține codul de producție lansat, iar develop ține linia de integrare unde converge munca în desfășurare. În jurul celor două apar și dispar trei familii de branch-uri scurte: feature branches pornite din develop, release branches pornite din develop când se pregătește un release și hotfix branches pornite din main când un bug în producție are nevoie de un patch rapid.

Un ciclu de viață tipic. Un developer creează feature/checkout-redesign din develop, lucrează o săptămână, deschide un pull request, primește review, face merge înapoi în develop. Mai multe feature branches se acumulează pe develop în câteva săptămâni. Când echipa e gata să livreze, un branch release/2.7.0 pornește din develop. Branch-ul de release acceptă doar bug fixes; munca pentru feature-uri noi continuă pe develop. Când branch-ul de release e stabil, se face merge în main, primește tag-ul v2.7.0 și se face merge înapoi în develop astfel încât orice fix din timpul release-ului să curgă mai departe. Un bug găsit ulterior în producție generează un hotfix/2.7.1 din main, e reparat, se face merge atât în main cât și în develop, iar ciclul continuă.

E multă ceremonie. Ceremonia există pentru că gitflow a fost proiectat pentru produse cu cicluri formale de release versionat: software shrink-wrapped, aplicații mobile care livrează prin review, software enterprise on-prem, biblioteci cu semver. În aceste contexte, separarea „ce e în development” de „ce am livrat” e o preocupare arhitecturală reală, iar branch-urile multiple permit echipei să lucreze la versiunea următoare în timp ce o suportă pe cea curentă.

GitHub flow

Documentația GitHub descrie un pattern mult mai simplu (https://docs.github.com/en/get-started/using-github/github-flow, consultat 2026-05-01). Un singur branch cu durată lungă de viață (main) ține cod gata de producție. Fiecare schimbare, oricât de mică, are loc pe un feature branch scurt pornit din main. Munca trăiește pe branch ore sau câteva zile. Se deschide un pull request, CI rulează suita de teste, un reviewer aprobă, branch-ul se face merge în main. Merge-ul declanșează adesea un deploy automat.

Cadența e continuă. Nu există „release branch” pentru că nu există un release formal: fiecare merge în main e un release, în sensul că e ce rulează acum. Pattern-ul se potrivește natural pentru SaaS și pentru majoritatea aplicațiilor web, pentru că suprafața de deployment e un singur sistem care rulează și pe care îl actualizezi continuu. Se potrivește și pentru majoritatea uneltelor interne și a data pipelines, unde ținta de deploy e un serviciu care rulează ultima versiune de cod.

GitHub flow e pattern-ul dominant în lumea open-source și în majoritatea organizațiilor moderne de inginerie sub câteva sute de ingineri. E și cel mai ușor de explicat cuiva nou la Git: „faci un branch, îți faci treaba, deschizi un PR, dai merge, ștergi branch-ul.”

Trunk-based development

Al treilea pattern merge cu un pas mai departe față de GitHub flow. Toată lumea face commit direct în main. Branch-urile încă există pentru code review, dar trăiesc ore, nu zile. Munca care nu e încă gata să fie expusă utilizatorilor se ascunde în spatele feature flags: codul e pe main, deployat și testat, dar un comutator runtime îl împiedică să ruleze până când echipa e gata să-l pornească.

Paul Hammant și alții au documentat pattern-ul la https://trunkbaseddevelopment.com (consultat 2026-05-01), bazându-se pe practicile organizațiilor mari de inginerie. Google, Facebook și Microsoft (pentru Azure DevOps și părți din stack-ul Office) rulează public monorepo-uri masive cu acest model. Etsy și Spotify sunt exemple la scară medie care au scris despre el. Firul comun e că la scară suficientă, alternativele încetează să funcționeze: branch-urile care trăiesc o săptămână intră în conflict cu tot ce e în jurul lor, iar rezolvarea conflictelor de merge începe să consume o fracțiune reală din timpul de inginerie.

Trunk-based development cere mai multă infrastructură decât GitHub flow. Feature flags devin esențiale, nu opționale. CI trebuie să fie suficient de rapid încât fiecare commit să fie validat în câteva minute. Code review trebuie să opereze pe schimbări mici, pentru că schimbările aterizează direct pe linia de integrare. Lecția 50 acoperă toate astea în detaliu.

Axele de comparație

Făcând un pas înapoi, cele trei strategii se separă curat pe patru axe.

Durata de viață a branch-ului. Gitflow are branch-uri cu durată lungă de viață: develop e permanent, feature branches trăiesc adesea săptămâni, release branches zile. GitHub flow are branch-uri cu durată medie: feature branches trăiesc ore până la câteva zile. Trunk-based are branch-uri scurte: ore, uneori minute. Cu cât durata de viață e mai scurtă, cu atât se acumulează mai puțină divergență între ce lucrezi și cum arată main.

Complexitatea de merge. Asta decurge direct din durata de viață a branch-ului. Branch-urile cu durată lungă intră în conflict cu tot ce e în jurul lor, deci a le da merge e o operațiune reală. Branch-urile scurte abia se desprind de main, deci merge-urile sunt de obicei fast-forward sau three-way merge-uri triviale. Echipele care urăsc merge-ul sunt de obicei echipele cu branch-uri care trăiesc prea mult.

Cadența de release. Gitflow presupune release-uri programate: echipa pregătește un release branch, îl stabilizează, îl livrează, repetă. GitHub flow presupune release-uri continue: fiecare merge în main e un release. Trunk-based presupune deployment continuu: fiecare merge poate fi deployat automat, adesea este, iar întrebarea „ce e în acest release” devine „ce era pe main la momentul deploy-ului”.

Tooling-ul cerut. Gitflow are nevoie de cel mai puțin tooling: modelul funcționează cu Git pur și un sistem CI. GitHub flow are nevoie de CI pe fiecare PR și ideal de deploy-uri automate. Trunk-based are nevoie de toate astea plus infrastructură de feature flags, plus un pipeline CI suficient de rapid și fiabil încât commit-ul direct în main să nu fie terifiant. Costul de tooling al trunk-based e real și e motivul pentru care echipele mici încep adesea cu GitHub flow și trec la trunk-based pe măsură ce scara o cere.

flowchart LR
    subgraph main ["main branch"]
        direction LR
        m1["init"] --> m2["small fix"] --> m3["merge login<br/>deploy"] --> m4["merge api<br/>deploy"] --> m5["merge dashboard<br/>deploy"]
    end

    subgraph login ["feature/login"]
        direction LR
        l1["login UI"] --> l2["login tests"]
    end

    subgraph api ["feature/api"]
        direction LR
        a1["api endpoint"]
    end

    subgraph dash ["feature/dashboard"]
        direction LR
        d1["dashboard"] --> d2["dashboard tests"]
    end

    m2 -.branch.-> l1
    l2 -.merge.-> m3
    m2 -.branch.-> a1
    a1 -.merge.-> m4
    m3 -.branch.-> d1
    d2 -.merge.-> m5

    classDef mainNode fill:#0d9488,stroke:#0d9488,color:#ffffff
    classDef branchNode fill:#1f2933,stroke:#52606d,color:#e8edf1
    classDef boundary fill:transparent,stroke:#0d9488,stroke-dasharray: 5 5
    class m1,m2,m3,m4,m5 mainNode
    class l1,l2,a1,d1,d2 branchNode
    class main,login,api,dash boundary

Diagram to create: a polished version of the GitHub flow gitGraph above. main is the central spine, three short-lived feature branches come off it and merge back in over a few days, and “deploy” markers sit on the merge commits. The visual point is that main is always shippable, branches are short, and merges happen continuously.

Când se potrivește fiecare strategie

Strategiile sunt unelte, nu religii. Potrivirea depinde de ce livrează echipa.

Gitflow se potrivește produselor cu release-uri versionate. Aplicații mobile unde review-ul de la App Store e poarta de deploy. Biblioteci cu semver unde utilizatorii fixează versiuni specifice. Software enterprise on-prem livrat trimestrial. Firmware embedded. Caracteristica comună e că există o diferență semnificativă între „ce am lansat” și „la ce lucrăm”, iar modelul de branching face acea diferență explicită. Ceremonia gitflow e potrivită când procesul de release subiacent e el însuși ceremonios.

GitHub flow se potrivește pentru majoritatea SaaS, majoritatea uneltelor interne și majoritatea data pipelines. Ținta de deploy e un singur sistem care rulează. Release-urile sunt continue. Echipa e suficient de mică încât branch-urile cu durată lungă să fie inutile. Infrastructura de feature flags poate exista, dar nu e o precondiție obligatorie. Aici se întâmplă cea mai mare parte a ingineriei profesionale și e default-ul sigur pentru o echipă care nu are un motiv specific să aleagă altceva.

Trunk-based se potrivește echipelor foarte mari sau echipelor angajate față de continuous deployment. Ajung aici două populații distincte. Prima sunt echipele suficient de mari încât branching-ul în general e dureros: sute de ingineri care fac commit într-un monorepo nu-și pot permite branch-uri de o săptămână, pentru că costul de merge domină. A doua sunt echipele cu o cultură puternică de feature flags care vor disciplina lui „totul pe main, controlat de un flag” pentru că elimină o întreagă clasă de buguri de integrare. Ambele populații obțin beneficii reale din pattern; ambele plătesc taxa de tooling pe care pattern-ul o cere.

Granița dintre GitHub flow și trunk-based e în practică fluidă. O echipă mică care folosește GitHub flow cu branch-uri de o zi și un loop CI rapid face aproape trunk-based. Diferența culturală e dacă echipa se gândește la main ca „branch-ul de integrare în care facem merge” (GitHub flow) sau „locul unde lucrăm, cu branch-urile ca formalitate de review” (trunk-based). Diferența mecanică e dacă feature flags sunt o parte de rutină a workflow-ului sau un caz special.

Particularitatea pentru data engineering

Data pipelines complică tabloul într-un mod specific: schema migrations și sistemele cu stare nu se rostogolesc înainte la fel de curat ca să codul fără stare.

Un serviciu web poate fi deployat de două ori pe zi, de zece ori pe zi, la fiecare commit. O pipeline care scrie într-un tabel de warehouse nu poate. Dacă schimbarea pe pipeline include o schema migration (o coloană nouă, o coloană redenumită, un tip schimbat), migrarea rulează o dată, contra unui warehouse real, iar rollback-ul înseamnă să anulezi migrarea, ceea ce uneori e imposibil. Strategia de branching interacționează cu asta în două feluri.

Primul, branch-urile cu durată lungă sunt mai riscante pentru codul de pipeline decât pentru codul de serviciu. Dacă branch-ul feature/customer-360 trăiește trei săptămâni cât echipa lucrează la un model dimensional nou, iar main livrează în săptămâna doi o altă schimbare de schemă, merge-ul branch-ului devine un exercițiu de criminalistică: care schemă e corectă, care migrare rulează prima, cum arată warehouse-ul în fiecare punct. Cu cât branch-ul e mai scurt, cu atât problema e mai mică.

Al doilea, feature flags sunt incomode pentru pipelines de batch. Un flag care controlează un cod path într-un request handler e ieftin; flag-ul e verificat, calea e luată, request-ul răspunde. Un flag care controlează o transformare într-un job batch nocturn e aceeași idee, dar consecințele unei valori greșite a flag-ului sunt persistente: datele au fost scrise, într-o formă sau alta, și nu le poți „de-scrie” fără un backfill. Trunk-based development pentru data pipelines cere o investiție în joburi idempotente (lecția 38) și mecanism de backfill fiabil (lecția 39) astfel încât output-urile „greșite” să poată fi regenerate.

Recomandarea practică pentru majoritatea echipelor de date e GitHub flow cu branch-uri scurte disciplinate, CI care rulează pipeline-ul pe sample data (lecția 51) și migrații controlate prin pași de deploy expliciți pe care echipa îi aprobă separat de merge-urile de cod. Trunk-based pentru data pipelines e realizabil, iar lecțiile ulterioare din Modulul 7 descriu pattern-urile, dar precondițiile sunt reale.

Unde ne duce asta

Strategia de branching e alegerea de suprafață. Sub ea, întrebarea mai profundă e cum se raportează echipa la linia de integrare: e main o destinație la care ajungi după pregătire sau un workspace în care trăiești continuu. Gitflow tratează main ca destinație. GitHub flow tratează main și ca workspace, și ca linie de deploy. Trunk-based tratează main ca workspace și pariază pe feature flags pentru a ține linia de deploy în siguranță.

Lecția 50 desface pattern-ul trunk-based în detaliu: de ce cele mai mari organizații de inginerie au convers spre el, care sunt precondițiile, cum funcționează feature flags și schimbarea culturală pe care pattern-ul o cere de la dezvoltatori. Lecția 51 trece apoi la CI pentru data pipelines, care e disciplina de testare ce face oricare dintre aceste strategii de branching să fie sigure într-un context de data engineering.

Citări și lecturi suplimentare

  • Vincent Driessen, „A successful Git branching model”, 2010, https://nvie.com/posts/a-successful-git-branching-model/ (consultat 2026-05-01). Propunerea originală gitflow, cu diagramele care au devenit referința standard.
  • GitHub Docs, „GitHub flow”, https://docs.github.com/en/get-started/using-github/github-flow (consultat 2026-05-01). Descrierea canonică a GitHub flow așa cum se practică pe platformă.
  • Paul Hammant și colaboratori, „Trunk-Based Development”, https://trunkbaseddevelopment.com (consultat 2026-05-01). Site-ul de referință pentru trunk-based development, inclusiv studiile de caz din organizațiile mari de inginerie.
  • Atlassian, „Comparing workflows”, https://www.atlassian.com/git/tutorials/comparing-workflows (consultat 2026-05-01). O comparație side-by-side neutră de furnizor a pattern-urilor majore, utilă ca referință de predare.
Caută