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 P1z wykorzystaniem bibliotek L1, L2i L3. L1również używa L2i L3, i również L2używa L3. Wykres zależności wygląda następująco:
<-------L1<--+
P1 <----+ ^ |
<-+ | | |
| +--L2 |
| ^ |
| | |
+-----L3---+
Teraz wyobraź sobie, cechą tego projektu wymaga zmiany P1i L3który zmienia interfejs L3. Teraz dodaj projekty P2i P3do 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ę
L3i poczekaj na recenzję - scal zmiany
- utwórz nową wersję
L3 - rozpocznij pracę nad funkcją
P1, odwołując się doL3nowej wersji, a następnie zaimplementuj funkcję wP1gałęzi funkcji - zgłoś żądanie ściągnięcia, sprawdź je i połącz
(Właśnie zauważyłem, że zapomniałem przełączyć L1i L2do 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?