Sposób organizacji i zarządzania kodem źródłowym jest jedną z kluczowych decyzji jaką musi podjąć zespół IT. Wybór ten decydował będzie nie tylko o szybkości implementowania i wdrażania zmian ale również o sposobie w jaki zorganizowana będzie praca członków zespołu. Jednym z podejść architektonicznych do zarządzania kodem źródłowym jest monorepo – pojęcie spopularyzowane w ostatnich latach przez międzynarodowe korporacje, które z powodzeniem wdrożyły to rozwiązanie w swoich organizacjach.
W Property Group zdecydowaliśmy się na wykorzystanie monorepo – dla projektów frontendowych – ponad 2 lata temu, przy okazji przepisywania portalu rynekpierwotny.pl. Nasza decyzja motywowana była intencją posiadania przejrzystego, łatwego w utrzymaniu i skalowalnego podejścia do zarządzania wieloma projektami, posiadającymi współdzielone fragmenty kodu źródłowego. Od 2020 roku pojawiło się wiele nowych rozwiązań wspierających wykorzystanie monorepo, a istniejące w tamtym czasie narzędzia doczekały się kolejnych wersji.
Ale czym właściwie jest monorepo? Jakie są alternatywne podejścia do zarządzania kodem źródłowym? Jakie są wady i zalety tego rozwiązania? Spróbujmy znaleźć odpowiedź na te i inne pytania w niniejszym artykule.
Czym jest monorepo?
Monorepo jest podejściem architektonicznym, w którym wiele projektów utrzymywanych jest w ramach jednego wspólnego repozytorium. Projekty mogą różnić się od siebie pod względem wykorzystywanego stacku technologicznego oraz mogą być rozwijane przez niezależne od siebie zespoły, przy jednoczesnym ułatwionym wykorzystaniu wspólnych fragmentów kodu.
Wspomniane łatwe współdzielenie kodu stanowi jedną z najważniejszych cech monorepo. Wydzielone fragmenty traktowane są jako oddzielne pakiety, a ich ewentualne wykorzystanie odbywa się wskutek dodania jako zależności, w projekcie docelowym.
Alternatywy dla monorepo
Architektura oparte na monorepo jest jedną z kilku opcji zarządzania kodem źródłowym – wśród alternatyw dla tego rozwiązania warto wymienić polyrepo, monolit oraz git submodules.
Przeciwieństwem monorepo jest polyrepo – rozwiązanie, w którym fragmenty kodu źródłowego wydzielone są do oddzielnych repozytoriów. Podział kodu na osobne repozytoria może być wynikiem wykorzystywanego stacku technologicznego , decyzji biznesowych oraz implementowanych funkcjonalności czy indywidualnych ustaleń zespołu IT.
Współdzielone fragmenty kodu mogą być publikowane w zdalnym repozytorium jako pakiety, a następnie dodane jako zależności w docelowym projekcie.
Koncepcyjnie – podobnym do monorepo podejście – jest monolit, gdzie również kod źródłowy wielu projektów zawarty jest w ramach jednego repozytorium. Główną różnicą monolitu względem monorepo jest fakt podziału kodu za pomocą odpowiedniej struktury folderów. Oddzielone na podstawie struktury folderów fragmenty kodu nie są traktowane jako oddzielne pakiety, a ich ewentualne wykorzystanie odbywa się za pomocą importu danego pliku w docelowe miejsce w projekcie.
Ciekawą alternatywą dla monorepo jest rozwiązanie znane jako git submodule, w którym niezależne od siebie repozytoria mogą być wykorzystane w jednym repozytorium nadrzędnym.
Inicjalizacja repozytorium jako submoduł, skutkuje jego dodaniem jako podfolder w głównym projekcie – przy czym kod źródłowy submodułu nie jest kopiowany, a zamiast tego tworzona jest referencja do repozytorium.
Zalety monorepo
- wspólna konfiguracja – monorepo daje możliwość posiadania jednej wspólnej konfiguracji środowiska deweloperskiego dla wielu aplikacji oraz pakietów. Konfiguracja narzędzi do statycznej analizy i formatowania kodu, procesu CI/CD czy środowiska uruchomieniowego testów jednostkowych – napisana jednokrotnie może być wykorzystana globalnie.
- współdzielenie kodu – podstawowa funkcjonalność monorepo, pozwalająca na łatwe i ponowne wykorzystanie fragmentów kodu.
- wersjonowanie – monorepo upraszcza kwestie wersjonowania pakietów, których wykorzystanie w ramach repozytorium może odbywać się bez definiowania konkretnej wersji. Odpowiedni zapis w pliku zależności pozwala zawsze korzystać z aktualnej wersji kodu w pakietach.
- globalne zmiany – implementacja globalnych zmian w ramach monorepo ogranicza się do przygotowania jednego merge request’u, a ewentualne breaking changes – za sprawą odpowiedniej konfiguracji – mogą być widoczne natychmiast po wprowadzeniu zmian w kodzie.
- zarządzanie zależnościami – monorepo w graficzny sposób pozwala zweryfikować drzewo zależności wszystkich aplikacji i pakietów. Za pomocą jednej komendy można m.in. wykonać aktualizację wersji pakietu we wszystkich projektach, które posiadają dany pakiet w pliku zależności.
- publikowanie pakietów – odpowiednia konfiguracja monorepo pozwala na proste publikowanie pakietów w zdalnym repozytorium. Cały proces może być wykonany za pomocą jednej komendy, która może uwzględnić dowolny zakres pakietów.
Przedstawione powyżej zalety monorepo budują obraz tego podejścia architektonicznego jako rozwiązania wpisującego się w coraz popularniejsze ostatnio zjawisko znane jako developer experience (DX). Łatwość wprowadzania globalnych zmian skutkująca oszczędnością czasu, zwiększona przejrzystość implementowanych funkcjonalności czy jednorazowa – reużywalna – konfiguracja wspólnych narzędzi – to jedne z wielu zalet wpływających na efektywność i zadowolenie programistów.
Wady monorepo
- zarządzanie dostępem – jedną z największych wad monorepo jest brak możliwości ograniczenia dostępu do poszczególnych fragmentów kodu źródłowego. Osoba otrzymująca uprawnienia do repozytorium posiada dostęp do całości kodu w nim zawartego.
- przejrzystość historii – rozwijanie wielu aplikacji i pakietów w ramach monorepo może prowadzić do zmniejszenia przejrzystości historii repozytorium. Wiele zespołów programistów współdzielących jedno repozytorium może generować znaczne ilości commitów oraz merge requestów.
- wielkość i czas inicjalizacji – znaczna ilość projektów rozwijanych w monorepo może doprowadzić do zwiększenia fizycznego rozmiaru repozytorium, a tym samym wydłużyć czas potrzebny na inicjalizację m.in. pobranie kodu czy instalacja wszystkich zależności.
- konfiguracja CI/CD – problemem, który może wynikać z dużej ilości projektów w jednym repozytorium jest skomplikowana i rozbudowana konfiguracja procesu CI/CD.
Podejście architektoniczne oparte na monorepo nie jest rozwiązaniem pozbawionym wad. O ile większość z opisanych powyżej kwestii dotyczy bardzo dużych projektów lub może być łagodzona za sprawą odpowiednich praktyk i narzędzi – to szczególnie istotna jest kwestia braku możliwości ograniczenia dostępu tylko do wybranych obszarów repozytorium. Z tego względu monorepo nie będzie efektywnym rozwiązaniem dla organizacji, w której konieczne jest wprowadzenie poziomów uprawnień dostępu do poszczególnych fragmentów kodu źródłowego.
Narzędzia
Obecnie dostępnych jest kilkanaście wiodących narzędzi służących do wsparcia pracy z monorepo. Ich głównym zadaniem jest zapewnienie efektywności pracy, przejrzystości oraz łatwego zarządzania monorepo przy jednocześnie rosnącej ilości kodu źródłowego. Szczegółowy zestaw funkcjonalności obejmuje:
- cache’owanie wyników wykonywanych zadań, zarówno w środowisku lokalnym jak i zdalnym
- zarządzanie zadaniami
- wizualizacja drzewa zależności
- współdzielenie fragmentów kodu
- spójność działania bez względu na stack technologiczny
- generowanie kodu dla powtarzalnych zadań
- ograniczenia wersji zależności oraz prywatność pakietów
Wśród narzędzi, które należy wymienić są Nx, Turborepo, Bazel, Lerna oraz Rush. Warto w tym miejscu również dodać, że wszystkie popularne menadżery pakietów JavaScript (NPM, Yarn oraz pNPM) posiadają własną implementację rozwiązań dla monorepo.
W Property Group zdecydowaliśmy się zaimplementować rozwiązanie oparte na narzędziu Yarn Workspaces, którego obecnie używamy w wersji 3.
Ponad dwa lata pracy z monorepo dostarczyły nam pozytywnych doświadczeń, co utwierdziło nas w przekonaniu o słuszności podjętej decyzji. Jedynie napotkane w ostatnich miesiącach trudności, związane z Yarn Workspaces, przyczyniły się do ponownego zweryfikowania aktualnie dostępnych rozwiązań wspierających pracę z monorepo. Podczas jednego ze spotkań technicznych uzgodniliśmy wspólny plan re-implementacji obecnego rozwiązania na manager pakietów pNPM, w połączeniu z narzędziem Nx – zmiana zostanie wykonana w ciągu najbliższych kilku tygodni.
Zakończenie
Monorepo wydaje się być obecnie jednym z najlepszych rozwiązań architektonicznych do zarządzania kodem źródłowym. Podejście to posiada szereg zalet, które mogą przyczynić się do zwiększenia efektywności pracy i zadowolenia programistów. Jednocześnie szeroki oraz intensywnie rozwijany zestaw narzędzi do zarządzania monorepo pokazuje stale rosnące zainteresowanie tym tematem wśród społeczności IT.