Kiedy rozdzielić projekt na wiele podprojektów

30

Chciałbym wiedzieć, czy sensownie jest podzielić projekt, nad którym pracuję, na dwa repozytoria zamiast jednego.

Z tego co mogę powiedzieć:

  • Frontend zostanie napisany w html + js
  • Backend w .net
  • Backend nie zależy od frontendu, a frontend nie zależy od backendu
  • Frontend użyje spokojnego interfejsu API zaimplementowanego w backend.
  • Frontend może być hostowany na dowolnym statycznym serwerze http.

Na razie repozytorium ma następującą strukturę:

korzeń:

  • frontend / *
  • backend / *

Myślę, że błędem jest utrzymywanie obu projektów w tym samym repozytorium. Ponieważ oba projekty nie mają zależności między sobą, powinny one należeć do poszczególnych repozytoriów, aw razie potrzeby do repozytorium nadrzędnego zawierającego podmoduły.

Powiedziano mi, że jest to bezcelowe i że nie uzyskamy z tego żadnej korzyści.

Oto niektóre z moich argumentów:

  • Mamy dwa moduły, które nie zależą od siebie.
  • Posiadanie historii źródłowej obu projektów na dłuższą metę może skomplikować sytuację (spróbuj wyszukać w historii coś w interfejsie, mając połowę zatwierdzeń, które są całkowicie niezwiązane z szukanym błędem)
  • Konflikt i scalanie (to nie powinno się zdarzyć, ale zmuszenie kogoś do pchnięcia backendu zmusi innego programistę do wycofania zmian backendu w celu pchnięcia zmian frontendu).
  • Jeden programista może działać tylko na backendie, ale zawsze będzie musiał wyciągnąć frontend lub na odwrót.
  • Na dłuższą metę, kiedy nadejdzie czas na wdrożenie. W pewien sposób interfejs może zostać wdrożony na wielu statycznych serwerach, mając jednocześnie jeden serwer zaplecza. W każdym przypadku ludzie będą zmuszeni albo do sklonowania całego backendu, albo do stworzenia niestandardowego skryptu, aby wypychał na wszystkie serwery tylko frontend lub usuwał backend. Łatwiej jest po prostu pchać / ciągnąć tylko frontend lub backend niż oba, jeśli tylko jeden jest potrzebny.
  • Przeciwdziałanie argumentom (jedna osoba może pracować przy obu projektach), Utwórz trzecie repozytorium z podmodułem i rozwijaj się z nim. Historia jest podzielona na poszczególne moduły i zawsze możesz tworzyć tagi, w których wersja backend / frontend naprawdę działa razem. Posiadanie obu frontend / backend razem w jednym repozytorium nie oznacza, że ​​będą one działać razem. To tylko połączenie obu historii w jedno duże repo.
  • Posiadanie frontendu / backendu jako submodułów ułatwi to, jeśli chcesz dodać freelancera do projektu. W niektórych przypadkach tak naprawdę nie chcesz dać pełnego dostępu do bazy kodów. Posiadanie jednego dużego modułu sprawi, że będzie trudniej, jeśli chcesz ograniczyć to, co „obcy” mogą zobaczyć / edytować.
  • Wprowadzenie błędu i naprawa błędu, wstawiłem nowy błąd w interfejsie. Następnie ktoś naprawia błąd w backend. W jednym repozytorium wycofanie przed nowym błędem spowoduje również wycofanie backendu, co może utrudnić jego naprawienie. Musiałbym sklonować backend w innym folderze, aby backend działał podczas naprawiania błędu w frontendzie ... a następnie próbowałem go przerobić ... Posiadanie dwóch repozytoriów będzie bezbolesne, ponieważ przemieszczenie HEAD jednego repo wygrało nie zmieniaj drugiego. A testowanie różnych wersji backendu będzie bezbolesne.

Czy ktoś może podać mi więcej argumentów, aby je przekonać lub przynajmniej powiedzieć mi, dlaczego dzielenie projektu na dwa podmoduły jest bezcelowe (bardziej skomplikowane). Projekt jest nowy, a baza kodów ma kilka dni, więc nie jest za wcześnie, aby to naprawić.

Loïc Faure-Lacroix
źródło

Odpowiedzi:

23

W mojej firmie używamy osobnego repozytorium SVN dla każdego komponentu systemu. Mogę powiedzieć, że robi się to wyjątkowo frustrujące. Nasz proces budowy ma tak wiele warstw abstrakcji.

Robimy to z Javą, więc mamy ciężki proces kompilacji z kompilacją javac, kompilacją wiązania JibX, sprawdzaniem poprawności XML itp.

Dla Twojej witryny może to nie być wielka sprawa, jeśli tak naprawdę jej nie „budujesz” (np. Waniliowy PHP).

Wady podziału produktu na wiele repozytoriów

  1. Zarządzanie kompilacją - nie mogę po prostu pobrać kodu, uruchomić samodzielnego skryptu kompilacji i mieć produkt, który można uruchomić / zainstalować / wdrożyć. Potrzebuję zewnętrznego systemu kompilacji, który przechodzi do wielu repozytoriów, uruchamia wiele wewnętrznych skryptów kompilacji, a następnie składa artefakty.
  2. Śledzenie zmian - sprawdzanie, kto zmienił co, kiedy i dlaczego. Jeśli poprawka błędu w interfejsie wymaga zmiany backendu, mam teraz dwie rozbieżne ścieżki, do których będę mógł wrócić później.
  3. Administracja - czy naprawdę chcesz podwoić liczbę kont użytkowników, zasad haseł itp., Którymi trzeba zarządzać?
  4. Scalanie - nowe funkcje prawdopodobnie zmienią wiele kodu. Dzieląc projekt na wiele repozytoriów, zwiększasz liczbę potrzebnych połączeń.
  5. Tworzenie gałęzi - podobnie jak rozgałęzianie, aby utworzyć gałąź, musisz teraz utworzyć gałąź w każdym repozytorium.
  6. Tagowanie - po udanym teście kodu chcesz oznaczyć wersję do wydania. Teraz musisz utworzyć wiele tagów, po jednym w każdym repozytorium.
  7. Trudno coś znaleźć - może frontend / backend jest prosty, ale staje się śliski. Jeśli podzielisz się na wystarczającą liczbę modułów, programiści mogą być zmuszeni zbadać, gdzie kawałek kodu znajduje się pod kontrolą źródła.

Mój przypadek jest nieco ekstremalny, ponieważ nasz produkt jest podzielony na 14 różnych repozytoriów, a każde repo jest następnie podzielone na 4-8 modułów. O ile pamiętam, mamy gdzieś około 80 lub jakieś „paczki”, które wszystkie należy sprawdzić indywidualnie, a następnie złożyć.

Twoja sprawa z tylko backendem / frontendem może być mniej skomplikowana, ale wciąż odradzam.

Ekstremalne przykłady mogą być przekonującymi argumentami za lub przeciw prawie wszystkim :)

Kryteria, których użyłbym do podjęcia decyzji

Rozważę podzielenie produktu na wiele repozytoriów kodu źródłowego po uwzględnieniu następujących czynników:

  1. Kompilacja - czy wyniki budowy każdego komponentu łączą się ze sobą, tworząc produkt? Podobnie jak łączenie plików .class z wielu komponentów w serię plików .jar lub .war.
  2. Wdrażanie - czy kończysz na komponentach, które są wdrażane razem jako jedna jednostka lub różne jednostki, które trafiają na różne serwery? Na przykład skrypty bazy danych trafiają na serwer DB, a javascript - na serwer WWW.
  3. Wspólna zmiana - czy często zmieniają się często czy razem? W twoim przypadku mogą się zmieniać osobno, ale nadal często.
  4. Częstotliwość rozgałęziania / scalania - jeśli wszyscy sprawdzają w pniu i gałęzie są rzadkie, być może uda ci się uciec. Jeśli często rozgałęziasz się i łączysz, może to zmienić się w koszmar.
  5. Zwinność - jeśli potrzebujesz opracować, przetestować, wydać i wdrożyć zmianę w mgnieniu oka (prawdopodobnie w przypadku SaaS), czy możesz to zrobić bez poświęcania cennego czasu na żonglowanie oddziałami i transakcjami repo?

Twoje argumenty

Nie zgadzam się również z większością twoich argumentów za tym podziałem. Nie będę kwestionować ich wszystkich, ponieważ ta długa odpowiedź będzie jeszcze dłuższa, ale kilka, które się wyróżniają:

Mamy dwa moduły, które nie zależą od siebie.

Nonsens. Jeśli zabierzesz swój backend, czy będzie on działał? Tak myślałem.

Długoterminowe posiadanie historii źródłowej obu projektów może skomplikować sytuację (spróbuj przeszukać historię w interfejsie, mając połowę zatwierdzeń, które są całkowicie niezwiązane z szukanym błędem)

Jeśli katalog główny projektu jest podzielony na frontend / i backend /, możesz niezależnie przeglądać historię tych hierarchii.

Konflikt i scalanie (to nie powinno się zdarzyć, ale zmuszenie kogoś do popychania do backendu zmusi innego programistę do wyciągnięcia zmian backendu do zmian push frontendu.) Jeden programista może działać tylko na backendie, ale zawsze będzie musiał go ściągnąć lub w inny sposób na około.

Podział projektu na różne repozytoria nie rozwiązuje tego. Konflikt interfejsu i konflikt zaplecza nadal pozostawiają 2 konflikty, niezależnie od tego, czy jest to 1 repozytorium 2 razy, czy 2 repozytoria razy 1 konflikt. Ktoś nadal musi je rozwiązać.

Jeśli chodzi o to, że 2 repozytoria oznaczają, że deweloper frontendu może scalić kod frontendowy, podczas gdy program backendowy scala kod backendowy, nadal możesz to zrobić za pomocą pojedynczego repozytorium za pomocą SVN. SVN może łączyć się na dowolnym poziomie. Być może jest to ograniczenie gitowe lub rtęciowe (oznaczyłeś oba, więc nie wiesz, jakiego SCM używasz)?

Z drugiej strony

Po tym wszystkim powiedziałem, że widziałem przypadki, w których podział projektu na wiele modułów lub repozytoriów działa. Nawet raz opowiedziałem się za tym przy konkretnym projekcie, w którym zintegrowaliśmy Solr z naszym produktem. Solr oczywiście działa na osobnych serwerach, zmienia się tylko wtedy, gdy zestaw zmian jest związany z wyszukiwaniem (nasz produkt robi znacznie więcej niż wyszukiwanie), ma osobny proces kompilacji i nie ma wspólnych artefaktów kodu ani artefaktów kompilacji.

Brandon
źródło
Umiar we wszystkim, jak mawiała moja matka ...
William Payne
W chwili pisania piszę frontend bez backendu. Emuluję backend za pomocą plików json i prawdopodobnie mógłbym nawet całkowicie emulować za pomocą indexedDB w przeglądarce. Więc tak, backend to tylko serwer obsługujący json. Można go zastąpić czymkolwiek, o ile otrzymane dane są zgodne z interfejsem API. Oba projekty używają innego systemu kompilacji. Krótko mówiąc, to tak, jakby mieć stronę internetową i mobilną aplikację na Androida. Dodanie aplikacji mobilnej do repozytorium serwera WWW.
Loïc Faure-Lacroix
Również jeśli nie było to jasne, backend i frontend nie są interfejsami użytkownika / administratora. Ale frontend to tylko interfejs ajax, a backend obsługuje json. Użytkownicy i role są obsługiwane inaczej, a interfejs administratora będzie znajdować się w interfejsie użytkownika. Chodzi o to, aby utrzymać obie części w izolacji i zapobiegać HTML wygenerowanemu javascript, aby załadować HTML wygenerowany przez serwer. Serwer powinien obsługiwać tylko pliki json lub xml.
Loïc Faure-Lacroix
1
Wówczas nie występują problemy z kompilacją ani wdrożeniem, więc może być w porządku. Ale znowu, jeśli dokonasz poważnej zmiany, być może będziesz musiał zmienić interfejs API, który wpływa zarówno na frontend, jak i backend, a zatem będziesz się rozgałęział dwukrotnie, scalał dwa razy, oznaczał dwa razy itd. Ale o ile pozostaje tylko dwa razy i nie zamieni się w 3 ... 4 ... 12 ... 20, prawdopodobnie niezły pomysł.
Brandon
Nawet jeśli interfejs API ulegnie zmianie, przy odpowiednim wersjonowaniu może być możliwe utworzenie wersji gałęzi dla każdego interfejsu obsługującego wersję interfejsu API. Backend powinien mieć pewną „wsteczną” kompatybilność i utrzymywać stary interfejs API tak długo, jak to możliwe.
Loïc Faure-Lacroix
3

Niektóre z twoich argumentów są prawidłowe, a niektóre nie.

Mamy dwa moduły, które nie zależą od siebie.

To nie jest do końca prawda. Aby móc się komunikować, zarówno front-end, jak i back-end muszą mieć wspólny interfejs (opis). To sprawia, że ​​jest to słaby argument przemawiający za posiadaniem obu w jednym repozytorium. Ale tylko słaby argument, ponieważ nie robi tak dużej różnicy.

Posiadanie historii źródłowej obu projektów na dłuższą metę może skomplikować sytuację (spróbuj wyszukać w historii coś w interfejsie, mając połowę zatwierdzeń, które są całkowicie niezwiązane z szukanym błędem)

To fałszywy argument. Jeśli chcesz sprawdzić, w jaki sposób naprawiono konkretny błąd, zajrzyj do narzędzia do śledzenia błędów, dla którego zatwierdzenie zawiera poprawkę. A jeśli chcesz wiedzieć, jak ewoluował dany fragment kodu, zapoznaj się z historią jednego pliku (lub co najwyżej garstki). W obu przypadkach posiadanie innych plików, prawdopodobnie z innych modułów, w repozytorium nie powinno w żaden sposób komplikować rzeczy.

Konflikt i scalanie (to nie powinno się zdarzyć, ale zmuszenie kogoś do pchnięcia backendu zmusi innego programistę do wycofania zmian backendu w celu pchnięcia zmian frontendu).

To fałszywy argument. Nie znam żadnego (na wpół przyzwoitego) VCS, w którym trzeba zsynchronizować całe repozytorium, zanim będzie można zatwierdzić / wypchnąć zmiany. Co najwyżej musisz zsynchronizować foldery zawierające zmienione pliki (a często tylko same pliki).

Jeden programista może działać tylko na backendie, ale zawsze będzie musiał go ściągnąć lub na odwrót.

To ten sam fałszywy argument, co poprzedni.

Na dłuższą metę, kiedy nadejdzie czas na wdrożenie. W pewien sposób interfejs może zostać wdrożony na wielu statycznych serwerach, mając jednocześnie jeden serwer zaplecza. W każdym przypadku ludzie będą zmuszeni albo do sklonowania całego backendu, albo do stworzenia niestandardowego skryptu, aby wypychał na wszystkie serwery tylko frontend lub usuwał backend. Łatwiej jest po prostu pchać / ciągnąć tylko frontend lub backend niż oba, jeśli tylko jeden jest potrzebny.

W zależności od tego, jak ludzie przewidują, że wdrożenie zostanie wykonane, może to być prawidłowy argument. Jeśli wdrożenie zostanie wykonane przez rozpakowanie pliku zip / tarballa na serwerze, nie ma znaczenia, w jaki sposób zorganizowane są repozytoria. Jeśli wdrożenie zostanie wykonane poprzez wyewidencjonowanie (części) repozytorium na serwerze, dobrym pomysłem może być użycie osobnych repozytoriów dla modułów, które są wdrażane osobno.

Przeciwdziałanie argumentom (jedna osoba może pracować przy obu projektach), Utwórz trzecie repozytorium z podmodułem i rozwijaj się z nim. Historia jest podzielona na poszczególne moduły i zawsze możesz tworzyć tagi, w których wersja backend / frontend naprawdę działa razem. Posiadanie obu frontend / backend razem w jednym repozytorium nie oznacza, że ​​będą one działać razem. To tylko połączenie obu historii w jedno duże repo.

To jest ważny argument, ale nie jest tak silny.

Posiadanie frontendu / backendu jako submodułów ułatwi to, jeśli chcesz dodać freelancera do projektu. W niektórych przypadkach tak naprawdę nie chcesz dać pełnego dostępu do bazy kodów. Posiadanie jednego dużego modułu sprawi, że będzie trudniej, jeśli chcesz ograniczyć to, co „obcy” mogą zobaczyć / edytować.

To jest poprawny argument.

Wprowadzenie błędu i naprawa błędu, wstawiłem nowy błąd w interfejsie. Następnie ktoś naprawia błąd w backend. W jednym repozytorium wycofanie przed nowym błędem spowoduje również wycofanie backendu, co może utrudnić jego naprawienie.

To fałszywy argument, ponieważ oznaczałoby to, że po dwóch poprawkach błędów w jednym module nie będzie można przywrócić pierwszego. Każdy na wpół przyzwoity VCS pozwoli ci cofnąć prawie każdy stary zatwierdzenie (chociaż często będzie to oznaczać, że wykonasz nowe zatwierdzenie, które odwraca te zmiany, czasem nawet na górze HEAD).

Musiałbym sklonować backend w innym folderze, aby backend działał podczas naprawiania błędu w frontendzie ... a następnie próbowałem go przerobić ... Posiadanie dwóch repozytoriów będzie bezbolesne, ponieważ przemieszczenie HEAD jednego repo wygrało nie zmieniaj drugiego. A testowanie różnych wersji backendu będzie bezbolesne.

To właściwie całkiem niezły argument. Posiadanie dwóch repozytoriów ułatwia testowanie scenariuszy, w których wdrożone interfejsy i zaplecze mogą (nieznacznie) zostać zsynchronizowane.

Bart van Ingen Schenau
źródło
Szczerze mówiąc, większość fałszywych argumentów można rozwiązać za pomocą gałęzi. Oddział dla frontendu i oddział dla backendu. Mistrz do synchronizacji. Ale w pewnym sensie obsługa takich gałęzi sprawia, że ​​jest to bardziej skomplikowane niż posiadanie dwóch repozytoriów.
Loïc Faure-Lacroix
1
@Sybiam: W rzeczywistości są to fałszywe argumenty, ponieważ nie podkreślają problemu, który mógłby istnieć przy korzystaniu z pojedynczego repozytorium, nawet jeśli wszystkie zmiany zostaną wprowadzone tylko w trunk / main.
Bart van Ingen Schenau
Myślę, że twoje krytyki są ważne. Po prostu nie sądzę, że o to chodziło w tym pytaniu.
sylvanaar
2

Ten post jest trochę stary, ale chciałbym się do niego przyczynić. Chociaż Twoje zaplecze tak naprawdę nie wie o interfejsie, musi ono zawierać żądania pasujące do interfejsu API zaplecza. Jeśli weźmiesz pod uwagę back-end jako interfejs API REST, możesz zdefiniować plik interfejsu, taki jak interfejs YAML swagger. Teraz są naprawdę 3 projekty, które możesz indywidualnie podzielić na różne repozytoria według własnego uznania:

  • Definicja API
  • Back-end
  • Front-end

Definicja API jest zależnością w dwóch pozostałych projektach, powiedzmy, że używałeś maven jako narzędzia do wstrzykiwania zależności. To zależy od tego, jak ściśle chcesz tworzyć wersje. Możesz podnieść wersję projektu definicji API za każdym razem, gdy wprowadzasz zmiany, aby upewnić się, że projekty są zawsze w stanie kompatybilnym, ale wymagają większego obciążenia, lub możesz użyć czegoś takiego jak SNAPSHOTS w maven i wykonać wersjonowanie tylko wtedy, gdy jesteś zadowolony z interfejsu, który jest mniej obciążony, ale często możesz mieć niezgodności. Ale tak długo, jak wymusisz definicję interfejsu API w interfejsie i zapleczu, będziesz mógł dobrze podzielić projekty na różne repozytoria.

Te problemy dotyczą bardziej zarządzania zależnościami. Nawet jeśli projekty nie są podzielone i znajdują się w tym samym repozytorium, dość łatwo jest wprowadzić stronę internetową w stan, w którym przód i tył nie są zsynchronizowane. Naprawdę jedynym sposobem na powstrzymanie tego jest faktyczne zdefiniowanie umowy między nimi, ale chcesz to zrobić w sposób, który nie łączy implementacji frontonu i back-endu, podobnie jak koduje się interfejs implementacji w programowaniu OO.

Aby zapobiegawczo poradzić sobie z krytyką, że stwarza to narzut związany z utrzymywaniem tego pliku interfejsu, na przykład swagger może nawet tworzyć kody pośredniczące kodu dla różnych języków programowania i struktur, takich jak JAX-RS. Możesz więc stworzyć interfejs w wybranej technologii, a następnie wdrożyć ten interfejs. Dodaje również bardzo ładną dokumentację do zaplecza, ułatwiając programistom front-end wykonywanie swoich zadań.

Snickers3192
źródło