Co to jest trunk based development? Przewodnik dla solo deva

Summary

Co to jest trunk based development? To model pracy, w którym zespół scala małe zmiany do jednej wspólnej gałęzi co najmniej raz dziennie, a niedokończoną pracę chowa za flagami funkcji. Trunk zostaje gotowy do wydania przy każdym commicie. Dla solo deva i małych zespołów oznacza to mniej konfliktów, szybszy feedback z CI i prostsze review. Warunek: testy, którym ufasz, i szybki sposób na cofnięcie zmian.

Ciemne biurko z monitorem pokazującym jedną świecącą pionową linię, metafora jednego wspólnego trunk

Co to jest trunk based development? To model pracy z gałęziami, w którym wszyscy scalają małe zmiany do jednej wspólnej gałęzi, zwykle nazywanej main albo trunk, co najmniej raz dziennie. Gałęzie, jeśli w ogóle je tworzysz, żyją godziny, a nie tygodnie. Niedokończona praca trafia do kodu schowana za flagą, więc trunk zostaje gotowy do wydania przy każdym commicie. To cała idea i jest mniej egzotyczna, niż sugeruje nazwa.

Oto scena, która to dobrze pokazuje. Otwierasz side project, który zostawiłeś trzy tygodnie temu. Gałąź z funkcją jest 41 commitów za main, dwa pliki mają konflikty, a to, co budowałeś, nie pasuje już do reszty kodu. Trunk based development to nawyk, który nie dopuszcza do takiej sceny.

Co tak naprawdę oznacza trunk based development?

Serwis trunkbaseddevelopment.com opisuje to jako model kontroli wersji, w którym programiści pracują na jednej wspólnej gałęzi i unikają innych gałęzi żyjących długo. Bezpośrednie commity do trunk są w porządku w bardzo małych zespołach. Pull requesty też, o ile gałęzie są krótkotrwałe i powstają z pracy jednej osoby.

Za większość tego modelu odpowiadają trzy zasady.

Zauważ, czego tu brakuje. Nie ma gałęzi develop, nie ma długiej gałęzi wydaniowej dla każdej funkcji, nie ma fazy integracji na końcu, kiedy wszyscy odkrywają, że ich kod do siebie nie pasuje. Przy starszych modelach ból się kumuluje i spada na raz.

Wysokie drzewo z jednym grubym pniem i tylko kilkoma krótkimi gałęziami

Dlaczego to lepsze niż długo żyjące gałęzie w małych zespołach?

Długo żyjąca gałąź to pożyczka. Każdego dnia oddala się trochę od main, a odsetki płacisz konfliktami przy scalaniu, nieaktualnymi założeniami i review, których nikt nie domknie, bo diff dotyka 60 plików.

Strona DORA o trunk-based development wiąże te praktyki z lepszą szybkością dostarczania, stabilnością i dostępnością, na podstawie badań State of DevOps. Według niej warto mieć trzy lub mniej aktywnych gałęzi w repozytorium, scalać do trunk przynajmniej raz dziennie i unikać zamrożeń kodu oraz faz integracji. To konkretne, mierzalne cele, a w tej branży rzadko się je spotyka.

Dla solo dewelopera albo zespołu z trzech osób argument jest jeszcze prostszy. Małe scalenia łatwo przejrzeć, łatwo cofnąć i łatwo zrozumieć pół roku później, gdy o 22 czytasz git blame i zastanawiasz się, co myślał sobie wtedy ty.

Trzy powody, żeby to zrobić, i jeden, żeby tego nie robić. Powody: mniejsze diffy, szybszy feedback z CI i main, który możesz wdrożyć w piątek bez nerwów. Powód przeciw: jeśli testy są cienkie, codzienne scalanie tylko szybciej rozprzestrzenia błędy. Do tego wrócimy niżej.

Krótkotrwałe gałęzie czy commit prosto do main?

Oba warianty to trunk based development. Różnica polega na tym, ile bezpieczeństwa chcesz mieć, zanim kod trafi do wspólnej historii.

Commit bezpośrednio do main działa, gdy pracujesz sam albo w parze, lokalny build trwa poniżej minuty i ufasz swoim testom. To najszybsza pętla, jaka istnieje. Większość solo builderów już tak robi, nawet nie nazywając tego w ten sposób.

Krótkotrwałe gałęzie z pull requestem mają sens, gdy dołącza druga albo trzecia osoba, albo gdy chcesz, żeby CI odpaliło się na czystym checkoucie przed scaleniem. Cała dyscyplina siedzi w słowie krótkotrwałe. Gałąź, która przetrwa do następnego dnia, już dryfuje w stronę modelu, z którego chcesz uciec.

Praktyczna rutyna, która się sprawdza:

Jeśli zmiany nie da się zrobić w jeden dzień, odpowiedzią nie jest dłuższa gałąź. Odpowiedzią jest pocięcie zmiany na cieńsze plasterki.

W praktyce najtrudniej zauważyć, że gałąź jest za długa, zanim zacznie boleć. Dobry sygnał to moment, w którym zaczynasz wątpić, czy twoja zmiana w ogóle pasuje do obecnego kodu. Wtedy scalenie jest już nerwowe, a nie rutynowe. Zapytaj się wcześniej, co w tej zmianie da się wydzielić jako osobny, bezpieczny krok: zmiana nazwy, dodany test, wydzielona funkcja albo nowy element schowany za flagą. Każdy taki krok możesz scalić dziś, a reszta zmiany poczeka w kolejce, zamiast gnić na osobnej gałęzi.

Jak wysłać niedokończoną pracę, nie psując main?

To pytanie zatrzymuje większość ludzi. Jeśli scalasz codziennie, będziesz scalać kod funkcji, które jeszcze nie istnieją. Radzą sobie z tym dwie techniki.

Pierwsza to flaga funkcji. Nowa ścieżka kodu jest scalona, ale wyłączona, więc użytkownicy jej nie widzą. Flaga może być równie prosta jak zmienna środowiskowa typu boolean:

const showNewCheckout = process.env.FEATURE_NEW_CHECKOUT === "true";

export function Checkout() {
  return showNewCheckout ? <NewCheckout /> : <OldCheckout />;
}

Na side project to wystarczy. Gdy potrzebujesz kierowania ruchu, stopniowych wdrożeń albo wyłącznika awaryjnego bez ponownego deployu, zarządzana usługa zaczyna się opłacać.

Druga technika to branch by abstraction, do większych zmian, na przykład wymiany klienta bazy danych albo dostawcy płatności. Stawiasz interfejs przed starą implementacją, budujesz nową za tym samym interfejsem, przenosisz wywołania fragment po fragmencie, a potem usuwasz starą. Każdy krok to małe scalenie do trunk i nic nie leży w długiej gałęzi.

Panel fizycznych przełączników, część włączona, część wyłączona, jak flagi funkcji

Jedna uczciwa uwaga o flagach: to dług. Flaga, która siedzi w kodzie przez rok, jest gałęzią w przebraniu. Przypisz każdej flagi właściciela i datę usunięcia, a gdy funkcja jest w pełni wdrożona, skasuj ją.

Czego potrzebuje twoje CI, zanim spróbujesz?

Trunk based development bez siatki bezpieczeństwa to po prostu szybkie psucie rzeczy. Zanim zobowiążesz się do codziennego scalania, potrzebujesz trzech rzeczy.

Analiza statyczna to tania dodatkowa warstwa. Wyłapuje klasę problemów, takich jak oczywiste błędy, podejrzane wzorce bezpieczeństwa i zduplikowany kod, zanim człowiek przeczyta diff.

Jeśli jesteś jedynym recenzentem, narzędzia wskazujące ryzykowne miejsca pomagają jeszcze bardziej, bo nikt inny nie powie ci, że plik, który ciągle edytujesz, zamienia się w bałagan.

Nie zakładaj, że pierwszego dnia potrzebujesz ciężkiego pipeline'u. Jeden plik workflow, który instaluje zależności, lintuje, testuje i wdraża main, to dobry start. Działa, nie jest optymalny i warto to zrobić mimo to: to pierwsza wersja, która działa, a zaostrzysz ją później.

Gałęzie wydaniowe, hotfixy i rzeczy, które wciąż wymagają uwagi

Trunk based development nie zabrania gałęzi wydaniowych. Niektóre zespoły wycinają gałąź z trunk tuż przed wydaniem, utwardzają ją, wypuszczają i usuwają. Inne wydają prosto z trunk i naprawiają do przodu, gdy pojawi się błąd. Dla aplikacji webowej wdrażanej kilka razy w tygodniu naprawianie do przodu jest niemal zawsze prostsze.

Aplikacje mobilne i wszystko, co przechodzi powolną weryfikację w sklepie, to inna historia. Tam można trzymać krótkotrwałą gałąź wydaniową dla każdej wersji, żeby łatać to, co już jest u użytkowników. Nawet wtedy zasada zostaje: poprawki lądują najpierw na trunk, a potem są przenoszone cherry-pickiem, nigdy odwrotnie.

Hotfixy działają tak samo. Napraw na trunk, wdróż, gotowe. Osobna gałąź hotfix, która nigdy nie wraca do main, to sposób, w jaki zespoły dostają dwie rozjeżdżające się historie.

Laptop z ciemnym edytorem kodu na kawiarnianym stoliku w nocy

Jak wygląda realistyczny pierwszy tydzień?

Pierwszego dnia sprawdź, jak dziś wygląda rozgałęzianie w twoim repo. Wypisz otwarte gałęzie i zanotuj wiek każdej. Wszystko starsze niż kilka dni to kandydat do scalenia albo usunięcia. Samo to często pokazuje, gdzie leży tarcie.

Drugiego dnia dodaj mechanizm flag. Nawet zmienna środowiskowa wystarczy. Wybierz jedną funkcję w toku, schowaj jej punkt wejścia za flagą i scal to, co masz.

Dni trzeci do piątego to zmiana nawyku. Tnij każde zadanie na kawałki, które mogą wylądować w ciągu dnia, i scalaj każdy kawałek, gdy CI przejdzie. Spodziewaj się, że pierwsze kawałki będą wydawać się za małe. Nie są. Commit, który dodaje uśpioną kolumnę w bazie albo nieużywaną funkcję, to w pełni dobra jednostka postępu, bo jest bezpieczna, przejrzana i już zintegrowana.

Pod koniec tygodnia będziesz wiedzieć dwie rzeczy: czy twoje testy są wystarczająco wiarygodne i które części kodu opierają się małym zmianom. Obie są przydatne, a żadna nie wychodzi na jaw, gdy pracujesz na długich gałęziach.

Nie potrzebujesz dashboardu, żeby to ocenić. Patrz, jak długo żyją twoje gałęzie, jak często main jest czerwony i ile czasu zajmuje zmiana od pierwszego commita do produkcji. Jeśli wiek gałęzi maleje, a wdrożenia stają się nudne, nawyk się opłaca.

Jeśli chcesz liczb o wpływie na użytkowników tego, co wysyłasz za flagami, narzędzie do eksperymentów może powiązać rollout z realnymi metrykami zamiast z przeczuciem.

Dla zespołu jednoosobowego zwykły zeszyt też wystarczy. Zapisuj co tydzień czas od scalenia do wdrożenia przez miesiąc. Trend powie ci więcej niż jakakolwiek pojedyncza liczba.

Przejść na to czy poczekać?

Przejdź, jeśli pracujesz sam, w parze albo w małym zespole z testami, którym ufasz tylko połowicznie, i CI, które da się poprawić. Koszt próby to tydzień lekko niewygodnych, małych commitów. Koszt długich gałęzi płacisz konfliktami i wolnymi review, a rośnie on z każdym współpracownikiem.

Poczekaj, jeśli nie masz żadnych automatycznych testów i nie masz jak cofnąć zmian. Najpierw to napraw, bo codzienne scalanie do nieprzetestowanego trunk tylko przesuwa ból w inne miejsce.

To nie jest idealna metoda. Jest wykonalna. Wybierz następną małą zmianę w swoim obecnym projekcie, scal ją dziś i zobacz, jak to wygląda. Jaki jest najmniejszy kawałek twojej obecnej funkcji, który mógłbyś wysłać jeszcze dziś wieczorem?

Frequently asked questions

Czy trunk based development oznacza brak gałęzi?
Nie. Krótkotrwałe gałęzie są dozwolone, o ile żyją godziny albo jeden dzień i są scalane do trunk. Chodzi o to, żeby żadna gałąź nie żyła tygodniami.
Czy solo developer może commitować bezpośrednio do main?
Tak, pod warunkiem że lokalny build trwa poniżej minuty i masz testy, którym ufasz. Wtedy dodatkowy pull request zwykle niewiele wnosi.
Czym trunk based development różni się od GitFlow?
GitFlow opiera się na długo żyjących gałęziach develop i release. Trunk based development trzyma jedną wspólną gałąź, a zmiany w toku rozbija na małe, szybko scalane kawałki.
Ile razy dziennie trzeba scalać do trunk?
Według zaleceń DORA przynajmniej raz dziennie, przy co najwyżej trzech aktywnych gałęziach w repozytorium.
Jak długo może żyć flaga funkcji?
Tylko tak długo, jak trwa wdrażanie funkcji. Każdej flagi przypisz właściciela i datę usunięcia, a po pełnym wdrożeniu usuń ją z kodu.
Co z gałęziami wydaniowymi?
Mogą istnieć, zwłaszcza w aplikacjach mobilnych, które przechodzą powolną weryfikację. Poprawki zawsze lądują najpierw na trunk i są przenoszone do gałęzi wydaniowej cherry-pickiem.