Robimy projekty, ale ponownie wykorzystujemy dużo kodu między projektami i mamy wiele bibliotek, które zawierają nasz wspólny kod. Wdrażając nowe projekty, znajdujemy więcej sposobów na wyróżnienie wspólnego kodu i umieszczenie go w bibliotekach. Biblioteki zależą od siebie, a projekty zależą od bibliotek. Każdy projekt i wszystkie biblioteki używane w tym projekcie muszą używać tej samej wersji wszystkich bibliotek, do których się odnoszą. Jeśli wydamy oprogramowanie, będziemy musieli naprawiać błędy i być może dodawać nowe funkcje na wiele lat, czasem na dziesięciolecia. Mamy około tuzina bibliotek, zmiany często dotyczą więcej niż dwóch, a kilka zespołów pracuje nad kilkoma projektami równolegle, wprowadzając jednocześnie zmiany we wszystkich tych bibliotekach.
Niedawno przeszliśmy na git i skonfigurowaliśmy repozytoria dla każdej biblioteki i każdego projektu. Używamy skrytki jako wspólnego repozytorium, robimy nowe rzeczy na gałęziach funkcji, a następnie wysyłamy żądania ściągania i scalamy je dopiero po przejrzeniu.
Wiele problemów, z którymi mamy do czynienia w projektach, wymaga zmian w kilku bibliotekach i specyficznym kodzie projektu. Często obejmują one zmiany interfejsów bibliotek, z których niektóre są niezgodne. (Jeśli uważasz, że to brzmi podejrzanie: łączymy się ze sprzętem i ukrywamy konkretny sprzęt za ogólnymi interfejsami. Niemal za każdym razem, gdy integrujemy sprzęt innego dostawcy, spotykamy się z przypadkami, których nasze interfejsy nie przewidywały i dlatego musimy je udoskonalić.) przykład, wyobrazić sobie projekt P1
z wykorzystaniem bibliotek L1
, L2
i L3
. L1
również używa L2
i L3
, i również L2
używa L3
. Wykres zależności wygląda następująco:
<-------L1<--+
P1 <----+ ^ |
<-+ | | |
| +--L2 |
| ^ |
| | |
+-----L3---+
Teraz wyobraź sobie, cechą tego projektu wymaga zmiany P1
i L3
który zmienia interfejs L3
. Teraz dodaj projekty P2
i P3
do miksu, które również odnoszą się do tych bibliotek. Nie możemy sobie pozwolić na przełączenie ich wszystkich na nowy interfejs, przeprowadzenie wszystkich testów i wdrożenie nowego oprogramowania. Więc jaka jest alternatywa?
- zaimplementuj nowy interfejs w
L3
- zgłoś prośbę
L3
i poczekaj na recenzję - scal zmiany
- utwórz nową wersję
L3
- rozpocznij pracę nad funkcją
P1
, odwołując się doL3
nowej wersji, a następnie zaimplementuj funkcję wP1
gałęzi funkcji - zgłoś żądanie ściągnięcia, sprawdź je i połącz
(Właśnie zauważyłem, że zapomniałem przełączyć L1
i L2
do nowej wersji. A ja nawet nie wiem gdzie trzymać to w, bo to muszą być wykonane równolegle P1
...)
Jest to żmudny, podatny na błędy i bardzo długi proces wdrażania tej funkcji, wymaga niezależnych recenzji (co znacznie utrudnia przeglądanie), w ogóle się nie skaluje i prawdopodobnie wyklucza nas z działalności, ponieważ ugrzęznąć w procesie, nigdy nic nie możemy zrobić.
Ale w jaki sposób wykorzystujemy rozgałęzianie i tagowanie, aby stworzyć proces, który pozwala nam wdrażać nowe funkcje w nowych projektach bez nadmiernego obciążenia?