Monorepo vs polyrepo : jak wybrać dla swojego zespołu
Summary
Debata monorepo vs polyrepo to tak naprawdę dyskusja o sprzężeniu kodu i koordynacji zespołów. Monorepo wygrywa gdy zespoły dzielą kod regularnie; polyrepo gdy pracują niezależnie. Rzeczywista odpowiedź zależy od mapy zależności, liczby inżynierów i wymogów bezpieczeństwa, a modern narzędzia (Turborepo, Nx, Bazel) sprawiają że każda strategia może zadziałać.
Monorepo vs polyrepo : jak wybrać dla swojego zespołu
Debata między monorepo a polyrepo pojawia się za każdym razem, gdy Twój zespół inżynierów rośnie szybciej niż Twoje czasy budowania. Prosta odpowiedź: monorepo wygrywam gdy zespoły regularnie dzielą kod i koordynują zmiany na wspólnej powierzchni; polyrepo gdy zespoły są rzeczywiście niezależne i wdrażają bez czekania na siebie nawzajem. Wszystko pomiędzy zależy od tego, jak silnie sprzężone są Twoje usługi i ile nakładu koordynacyjnego jesteś gotów zaakceptować. Ten artykuł przechodzi przez rzeczywiste kompromisy, sygnały wskazujące w jedną lub drugą stronę oraz praktyczną ramę do podjęcia decyzji.
Prawdziwe pytanie dotyczy sprzężenia, nie liczby repozytoriów
Większa część debat monorepo vs polyrepo zaczyna się od złego pytania. Liczba repozytoriów nie jest problemem. Problem jest taki: czy Twoje zespoły muszą poruszać się razem.
Jeśli zmiany Zespołu A rutynowo wymagają od Zespołu B zaktualizowania czegoś w tym samym cyklu wydawniczym, masz ściśle sprzężoną pracę. Jedno repozytorium dla tego jest niemal zawsze czystszym rozwiązaniem. Jeśli Zespół A i Zespół B wdrażają według swoich harmonogramów, dotykają różnych części systemu i nigdy na siebie nie czekają, osobne repozytoria mają rzeczywiście sens.
Najgorszy możliwy rezultat: dwa osobne repozytoria dla kodu, który wciąż musi być wdrażany razem. Otrzymujesz wszystkie koszty koordynacji monorepo bez żadnych korzyści. Twoje zespoły otwierają PRy w repozytorium A i repozytorium B jednocześnie, mając nadzieję, że obaj przejdą CI w tym samym czasie. Ten wzorzec jest zaskakująco częsty w organizacjach, które wybrały polyrepo wcześnie, a następnie pozwoliły, aby ich usługi stały się bardziej od siebie zależne niż pierwotnie planowano.
Pierwsza rzecz przed wybraniem czegokolwiek to narysowanie prostej mapy zależności. Wymień każdą usługę lub pakiet, którym zarządzasz i rysuj strzałki tam, gdzie jeden zależy od drugiego. Jeśli strzałki tworzą gęstą grupę, ta grupa powinna być w jednym repozytorium.

Dlaczego Google, Meta i Microsoft wybrały monorepo w skali
Google przechowuje większość swojego kodu w jednym repozytorium, szacowanym na ponad 80 TB danych i 2 miliardy linii kodu. Meta i Microsoft robią to samo dla swoich głównych powierzchni produktów. To nie dlatego, że dużym firmom podoba się złożoność : to dlatego, że w skali, synchronizowanie zależności między setkami repozytoriów staje się problemem inżynierii na cały etat.
Jedna zmiana API w wspólnej bibliotece może wymagać skoordynowanych aktualizacji w 40 różnych repozytoriach. W monorepo to jeden PR. Jedna recenzja. Jedno scalenie. Jedno cofnięcie się jeśli coś pójdzie nie tak.
Zgodnie z badaniami opublikowanymi przez Sourcegraph, 63% firm z 50 lub większą liczbą programistów używa teraz monorepo dla przynajmniej części swojego kodu, co stanowi znaczny wzrost w porównaniu z danymi sprzed trzech lat.
Konkretna korzyść, która ma największe znaczenie: atomic commits. Gdy przełomowa zmiana w Twoim API wymaga również aktualizacji frontendu i backendu, monorepo pozwala na scalenie wszystkich trzech zmian w jednym PRze, przetestowanie ich razem i cofnięcie wszystkich trzech jako jednostki jeśli coś pójdzie nie tak. Przy polyrepo otwierasz trzy osobne PRy, czekasz na trzy osobne cykle recenzji i zarządzasz oknem, w którym Twoje usługi działają w niedopasowanych wersjach.
Jeden zespół inżynierski zgłosił przejście od okazjonalnych wdrożeń do ponad 40 wydań aplikacji na tydzień po migracji swoich pakietów frontendu do monorepo Nx. Aktualizacje Angulara, które wcześniej zajmowały miesiące, stały się rutynowymi zadaniami, które zespoły obsługiwały bez zatrzymywania innej pracy.
Ukryte koszty koordynacji polyrepo
Polyrepo ma rzeczywiste przewagi warte poważnego rozpatrzenia. Każdy zespół kontroluje swój własny potok CI/CD, swój własny rytm wydań, swoje własne kontrole dostępu. Zespoły ds. bezpieczeństwa i zgodności często to preferują: dostęp do kodu można ograniczyć do wyznaczonych osób na zasadzie repozytoriów, co ma znaczenie w regulowanych branżach. Komponenty open-source żyją bardziej czyszczej w ich własnych publicznych repozytoriach bez ekspozycji wewnętrznego kodu.
Dla zespołów, które są rzeczywiście niezależne, te przewagi są rzeczywiste. Mały zespół usług, który obsługuje potok danych działający według własnego harmonogramu i nie dotyka żadnego wspólnego kodu, ma mało co zyskać na siedzeniu w większym monorepo.
Ale organizacje polyrepo cicho gromadzą koszty, które nie pojawiły się w oryginalnej decyzji. Wersjonowanie zależności między repozytoriami staje się własną dyscypliną. Wspólna biblioteka narzędziowa staje się przestarzała w niektórych repozytoriach, podczas gdy w innych pozostaje aktualna. Ktoś musi utrzymywać macierz kompatybilności tylko po to, aby wiedzieć, która wersja której biblioteki działa z którą wersją której usługi.
Jedna metryka warta wiedzy: medianowy czas cyklu PR w monorepo wynosi około 19 godzin, w porównaniu z około 2 godzinami w polyrepo. PRy w monorepo są zwykle większe, ponieważ dotykają więcej powierzchni, co spowalnia recenzję. Ale gdy zmiana polyrepo wymaga trzech skoordynowanych PRów w trzech repozytoriach do wylądowania pojedynczej funkcji, zagregowany czas cyklu często przekracza cyfrę monorepo : z dodanym ryzykiem częściowych scaleń pozostawiających usługi w niespójnych stanach.
Wzorzec, który zaskakuje zespoły: "zaczęliśmy z osobnymi repozytoriami dla niezależności, ale teraz każde wydanie wymaga otwierania PRów w czterech miejscach." W tym punkcie masz najgorsze z obu światów.
Czasy budowania to już nie główny czynnik (2024)
Klasycze sprzeciw wobec monorepo dotyczył czasu budowania. Jeśli Twoje repozytorium ma 200 pakietów i zmieniasz jeden plik, naprawdę chcesz przebudować wszystkie 200?
Ten argument wygasł gdzieś około 2022 do 2024, w zależności od stosu.
Współczesne narzędzia budowania wykorzystują cache świadomy treści. Przebudowują tylko to, co się zmieniło, i pomijają wszystko z identycznymi wejściami. Zmiana pakietu payments nie wyzwala przebudowy design-system jeśli nic w design-system się nie zmieniło. GitHub Actions zapewnia 10 GB wolnego magazynu cache zanim będziesz musiał płacić za rozwiązanie cache'u zdalnego.
Turborepo obsługuje większość zespołów JavaScript i TypeScript dobrze aż do około 20 pakietów. Konfiguracja to jeden turbo.json, cache'owanie zdalne działa na Vercel za darmo lub samoobsługiwanie, a krzywa uczenia jest wystarczająco niska, aby zespół mógł być produktywny w ciągu dnia.
Powyżej 20 pakietów, lub gdy budowanie obejmuje wiele języków, bardziej ustrukturyzowane podejście Nx zwykle uzasadnia bardziej stromą krzywą uczenia. Nx dodaje generowanie kodu, automatyczną dystrybucję CI i śledzenie zależności o drobnoziarnistości, które stają się coraz bardziej wartościowe w miarę rozrostu repozytorium.
Dla bardzo dużych organizacji obsługujących tysiące pakietów w wielu językach, Bazel (system budowania open-source Google'a) to jedyne narzędzie zaprojektowane dla tej skali. Koszt konfiguracji jest znaczny i prawie zawsze wymaga dedykowanego zespołu inżynierii platform.

Cztery pytania rozstrzygające dla Twojego zespołu
Nie ma uniwersalnej słusznej odpowiedzi, ale jest niezawodna rama.
1. Czy Twoje zespoły dzielą kod, który zmienia się często? Jeśli tak : wspólny system projektowania, wspólna biblioteka klienta API, wspólna logika uwierzytelniania : monorepo jest niemal zawsze lepszym wyborem. Utrzymanie zsynchronizowanej biblioteki wspólnej w wielu repozytoriach wymaga stałej dyscypliny i prawie zawsze dryftuje.
2. Czy Twoje zespoły wdrażają niezależnie? Jeśli usługa Zespołu A zostaje wydana we wtorek, a Zespołu B w czwartek bez żadnej potrzeby koordynacji, polyrepo zasługuje na swoje miejsce. Jeśli wydanie jednej usługi wymaga równoczesnego wydania innej, ten koszt sprzężenia należy do równania.
3. Ilu inżynierów dotyka tego kodu? Poniżej 20 inżynierów struktura repozytorium ma mniej znaczenia niż myślisz. Powyżej 50, koszty koordynacji polyrepo zaczynają się gromadzić widoczenie. Powyżej 200, sprawa dla monorepo staje się bardzo silna, chyba że zespoły są rzeczywiście odizolowane według produktu i technologii.
4. Czy masz wymogi bezpieczeństwa lub zgodności, które izolują kod na zespół? W regulowanych branżach : finanse, opieka zdrowotna, niektóre konteksty rządowe : dostęp do kodu może wymagać ograniczenia do wyznaczonych osób. To ograniczenie może zmienić inne odpowiedzi. Jeśli twarda izolacja kodu jest wymogiem zgodności, polyrepo nie jest wyborem, to jest ograniczenie.
Jeśli odpowiedziałeś "tak" na pytanie 1 i "nie" na pytanie 4, monorepo jest niemal pewnością właściwym wyborem. Jeśli odpowiedziałeś "nie" na pytanie 1 i "tak" na pytanie 2, polyrepo jest obronnością. Wszystko inne to sprawa oceny.
Co rzeczywiście robią rosnące zespoły
Wielmi mało dojrzałych organizacji inżynierskich działa w któreś z ekstremów. Wzorzec, który pojawia się najczęściej: monorepo dla głównego produktu (frontend, wspólne biblioteki, usługi backend, które zależą od siebie) i osobne repozytoria dla rzeczywiście niezależnych komponentów.
Potok danych, który działa według własnego harmonogramu i nie dzieli żadnego kodu z głównym produktem, powinien być poza monorepo. Wewnętrzne narzędzie utrzymywane przez zespół jednoosobowy powinno być poza. Biblioteka open-source, która wymaga publicznej widoczności, powinna być poza.
To nie jest kompromis. To jest prawidłowa odpowiedź na pytanie, które rzadko ma czystą binarną odpowiedź. Trzymaj razem rzeczy, które muszą poruszać się razem. Oddzielaj to, co jest rzeczywiście niezależne.
Ważne jest podjęcie decyzji celowo, nie domyślnie. Większość zespołów, które kończą się z polyrepo rozsierdnięciem, nie wybrała tego: zaczęła jedną usługę, potem drugą, potem trzecią i nigdy się nie zatrzymała, aby zapytać, czy te usługi musiały żyć razem.
Narzędzia, które sprawiają, że to rzeczywiście działa
Dla monorepo:
Nx: najsilniejszy zestaw funkcji, dobry dla budowań poliglotycznych i zespołów powyżej 20 pakietów. Wyższa krzywa uczenia ale skaluje się dalej.
Turborepo: prostsze do uruchomienia, doskonałe dla zespołów JS/TS poniżej 20 pakietów. Natywne cache'owanie Vercel lub opcje samoobsługiwania.
Bazel: dla bardzo dużych wielojęzycznych repozytoriów w skali przedsiębiorstwa. Wymaga znacznego inwestycji w konfigurację.
Dla polyrepo:
Dyscyplina wersjonowania dla wspólnych bibliotek nie jest opcjonalna. Bez semantycznego wersjonowania i spójnego procesu dziennika zmian, dryft zależności staje się normą w ciągu sześciu miesięcy.
Narzędzia CI/CD, które wyzwalają potoki między repozytoriami gdy wspólna zależność się zmienia. GitHub Actions to obsługuje za pośrednictwem zdarzeń
workflow_dispatchirepository_dispatchmiędzy repozytoriami.Rejestr zależności (prywatny rejestr npm, GitHub Packages, Artifactory) do zarządzania dystrybucją pakietów wspólnych.
Biscuit może pobrać plik konfiguracyjny. Decyzja, która ma być użyta, wciąż zależy od tych czterech pytań.
