Jak refaktoryzować, gdy cały twój rozwój jest na gałęziach?

24

W mojej firmie cały nasz rozwój (poprawki błędów i nowe funkcje) odbywa się w osobnych oddziałach. Po zakończeniu wysyłamy go do działu kontroli jakości, który testuje go na tym oddziale, a kiedy dają nam zielone światło, łączymy go z naszym głównym oddziałem. Może to potrwać od jednego dnia do roku.

Jeśli spróbujemy wcisnąć dowolną refaktoryzację do gałęzi, nie wiemy, jak długo będzie ona „wyłączona”, więc może powodować wiele konfliktów, gdy zostanie ponownie włączona.

Na przykład, powiedzmy, że chcę zmienić nazwę funkcji, ponieważ funkcja, nad którą pracuję, intensywnie korzysta z tej funkcji, i stwierdziłem, że jej nazwa nie pasuje do jej celu (znowu, to tylko przykład). Rozglądam się i znajduję każde użycie tej funkcji, i zmieniam ich nazwy na nowe, a wszystko działa idealnie, więc wysyłam je do kontroli jakości.

W międzyczasie dzieje się nowy rozwój, a moja funkcja o zmienionej nazwie nie istnieje w żadnej gałęzi, która jest rozwidlana na główną. Kiedy mój problem zostanie ponownie włączony, wszystkie się zepsują.

Czy jest jakiś sposób na poradzenie sobie z tym?

To nie jest tak, że kierownictwo kiedykolwiek zatwierdzi kwestię dotyczącą wyłącznie refaktorów, więc trzeba ją wcisnąć w inne prace. Nie można go opracować bezpośrednio na głównym, ponieważ wszystkie zmiany muszą przejść przez kontrolę jakości i nikt nie chce być palantem, który złamał główny, aby mógł zrobić trochę nieistotnego refaktoryzacji.

mpen
źródło
Jakiej kontroli wersji używasz? Istnieją różne podejścia do DVCS i scentralizowanego modelu serwera. Co więcej, z czego zdejmowane są gałęzie rozwoju? Jeśli gałąź funkcji zostanie zaakceptowana, w jaki sposób inne gałęzie deweloperów zauważą zmiany?
2
Nawiasem mówiąc, schemat obecnej struktury rozgałęzień może być naprawdę pomocny. Jest całkiem możliwe, że źródło problemu z trudnościami z refaktoryzacją jest częściowo spowodowane przez ... niekonwencjonalne zasady rozgałęziania ( jeden przykład takiego programu znajduje się w programmers.stackexchange.com/questions/210360 ). Sugerowałbym również przeczytanie vance.com/steve/perforce/Branching_Strategies.html, aby uzyskać pomysły i zaplecze (jeśli będę w stanie odpowiedzieć na to pytanie, będzie to główny punkt odniesienia).
1
Ostatni akapit podsumowuje to - jeśli Firma nie dostrzeże wartości, nie ma możliwości, aby duży rafinator mógł przejść dalej. Musisz współpracować z zespołem testowym w celu ustalenia terminów. (Podejrzewam, że twoja kontrola jakości naprawdę sprawdza się w przeciąganiu (nakładają perukę i szminkę i udają, że są czymś, czym nie są). Prawdziwy zespół kontroli jakości powiedziałby ci, co należy refaktoryzować, nie wchodząc ci w drogę.)
Mattnz
1
@mattnz: Masz całkowitą rację. Nie są prawdziwym zespołem ds. Kontroli jakości. Są to głównie obsługa klienta. Myślę, że wiele z ich obowiązków powinno zostać przeniesionych z powrotem na zespół deweloperów, ponieważ po prostu nie mogą poradzić sobie ze wszystkim, co na nich rzucamy, ale to kwestia zarządzania i bitwa, którą jeszcze nie wygrałem.
mpen
3
Przegapiłeś moje wykopalisko. Test! = QA. Kontrola jakości nadzoruje jakość i ma na celu poprawę wyników biznesowych. Przetestuj próby udowodnienia braku wad, znajdując je.
mattnz

Odpowiedzi:

12

Istnieje kilka problemów, które łączą się ze sobą, aby refaktoryzacja była trudna w tym środowisku. Pomieszane są z tym pewne problemy nietechniczne („ale to kwestia zarządzania i bitwa, którą jeszcze nie wygrałem”).

Pierwszym problemem, na który należy zwrócić uwagę, jest długo działająca gałąź. Te oddziały mają trudności ze śledzeniem zmian poza zasięgiem dewelopera. Aby rozwiązać ten problem:

  • Kiedy kod jest kompletny - daj mu jeszcze raz (pozwól, aby obsługa klienta spojrzała na niego, jeśli chcą), ale szybko połącz go w celu opracowania, aby inne zależne od niego zmiany mogły zostać wykryte, a zmiany, które konflikt są wcześnie identyfikowane w trakcie.
  • Jeśli z jakiegoś powodu jakiś brach staje się długotrwały w czasie trwania refaktoryzacji, dobrą praktyką jest łączenie się ze stabilnej do gałęzi w celu wychwycenia zmian i refaktoryzacji. Często minimalizuje to konflikty i niespodzianki podczas łączenia z gałęzi funkcji do gałęzi stabilnej.
  • Wszystkie testy integracji muszą być wykonywane w wersjach - nie w funkcjach . W tym środowisku funkcje mogą, ale nie muszą być w pełni zintegrowane z systemem. Chociaż można wykonać kontrolę poprawności funkcji w oderwaniu, nie rozpoznaje ona problemów po wydaniu.
  • Od czasu ukończenia kodu do scalenia (nazwijmy go rozwijaniem - rozgałęzienie z master / stable / release ma swoje własne problemy z niezauważaniem najnowszych zmian programistycznych) nie powinno być zbyt długie. Im dłużej czekasz, tym więcej utraconej wiedzy i tym trudniej jest zintegrować kod z innymi liniami kodu.

Inną kwestią, która się z tym wiąże, jest to, że nawiązałem do powyższych punktów, to zmieniająca się rola gałęzi w miarę upływu czasu. Zaczyna się jako gałąź programistyczna, w której programiści się zobowiązują, a następnie staje się obszarem testowania (jakie testy są tutaj wykonywane, które mogą mieć znaczenie w całej aplikacji?), Które następnie są scalane w stabilne (i prawdopodobnie wydawane - czy to jest testowane ponownie?).

Dzięki krótszemu czasowi rozpoczęcia i zakończenia funkcji refaktoryzacja jest łatwiejsza do odbioru przez inne oddziały.

Zachęć programistów do zdobycia całego środowiska. Tylko wybierające zmiany mogą prowadzić do ... powiedzmy, interesujących środowisk programistycznych. Podczas gdy zbieranie wiśni ma swoje zastosowanie, jest to domyślny tryb wprowadzania zmian do gałęzi, co może być niepokojące.

Refaktoryzacja jest czymś, co idealnie odbywa się w sposób ciągły, a jeśli nie w sposób ciągły, ilekroć dochodzi do odrobiny przestojów. Rozgałęź, wykonaj proste refaktoryzowanie, uruchom testy jednostkowe, aby sprawdzić, czy wszystko nadal działa (jego jednostka przetestowana, prawda? Prawda? ), A następnie ponownie połącz w stabilne. Przekaż informacje innym programistom, aby wprowadzić zmiany, które zostały refaktoryzowane do ich własnych oddziałów.

Ważne jest, aby programiści posiadali jakość kodu. Podczas gdy kierunek funkcji pochodzi z zewnątrz, a przydziały czasu często nie są nasze, jakość kodu jest czymś, z czego trzeba być dumnym i mieć czas.

Przy poszukiwaniu czasu na spłatę długu technicznego przydatne mogą być następujące pytania:

Możesz także przyjrzeć się narzędziom, takim jak sonar, które mogą pomóc zidentyfikować obszary kodu, które wymagają najwięcej pracy przy refaktoryzacji. Wtyczki dług techniczny jest coś, co może być wykorzystane do punktu pomóc akumulacji długu w czasie w bazie kodu.

Często trzeba zauważyć, że zwrot z inwestycji w zadłużenie techniczne to szybszy czas realizacji funkcji i poprawek błędów od zespołu programistów.

Społeczność
źródło
Testy są zasadniczo wykonywane w trzech punktach czasowych. Raz, gdy problem zostanie zgłoszony jako naprawiony (aby upewnić się, że spełnia wszystkie wymagania i nie ma większych problemów), ponownie, gdy zostanie ponownie włączony do domyślnego (testy integracyjne), i ponownie, gdy wykonamy kompilację (integracja ze wszystkimi wybranymi przez Cherry) problemy / przegląd końcowy). Uważam, że w naszym środowisku niezbędna jest kompletacja, ponieważ obsługujemy SaaS z bardzo konkretnymi klientami. Rzucę okiem na te linki, dzięki za wskazówki! Edycja: Jest jeszcze jeden przegląd produkcji, aby upewnić się, że poszła OK.
mpen
3

Zazwyczaj rozwijam wersję refaktoryzowaną „równolegle” z prądem, tj. W tej samej bazie kodu, ale nie odwołując się do niej z podstawowej aplikacji. A kiedy nowe rozwiązanie jest gotowe i przetestowane, zaczynam faktyczne refaktoryzowanie.

Przykład 1. Załóżmy, że mam coś, niech to będzie funkcja, interfejs, moduł lub cokolwiek innego. I chcę to refaktoryzować. Tworzę Thing2 w tej samej bazie kodu, jest to refaktoryzowana wersja Thing. Kiedy jest to zrobione i przetestowane, refaktoryzuję wszystko, co odnosi się do Thing, aby zastąpić go Thing2. Zwykle ten krok zajmuje stosunkowo mało czasu.

Jeśli faktyczne refaktoryzacja zajmuje zbyt dużo czasu, aby zsynchronizować się bez wkręcania zespołu, biorę wszystkie istotne funkcje i refaktoryzuję je również równolegle.

Przykład 2. Mam nowy backend renderowania, czyli refaktoryzowaną wersję starego. Ale nie jest kompatybilny ze starym frontendem renderującym. Dlatego muszę zrefaktoryzować interfejs. I znowu: w tej samej bazie kodu. Kiedy wszystko zostanie zrobione, zmieniam tylko klasę instancji interfejsu, najlepiej zajmie to jedno krótkie zatwierdzenie.

Tak, rekurencyjnie można dojść do wniosku, że wszystko musi być wykonane równolegle. Ale zwykle dzieje się tak, gdy w bazie kodu jest za dużo sprzężenia lub zmienia się ono zbyt szybko.

Wreszcie, gdy nowy kod zostanie zintegrowany i działa dobrze, stare funkcje mogą zostać usunięte z bazy kodu, a nowe funkcje mogą zostać zmienione, aby uzyskać stare nazwy.

Ogólnie rzecz biorąc, pomysł polega na równoległym przygotowaniu nowych funkcji i przejściu do korzystania z nich o jeden mały krok.

John Carmack stosuje to (lub przynajmniej podobne) podejście, być może jego post na blogu wyjaśnia to lepiej: (link)

Shadows In Rain
źródło
To dobre podejście. Próbuję sobie przypomnieć, co tak naprawdę skłoniło to pytanie ... Nie sądzę, żeby było to coś bardzo podatnego na równoległość. A jeśli tak, myślę, że obawiam się, że takie podejście powoduje duże rozdrobnienie w bazie kodu. Mamy „stare sposoby” robienia rzeczy i „nowe sposoby” robienia rzeczy, a stare rzeczy są zastępowane w szybkim tempie, ale wywołują ból głowy dla programistów, ponieważ teraz w zasadzie muszą znać dwa (lub więcej) systemy.
mpen
1

Może to wyglądać na trudność techniczną, kiedy faktycznie jest po stronie wymagań.

Prawdziwą trudność stanowi rozwój ukierunkowany na różne wymagania w różnych branżach. Kierownicy i architekci zespołu powinni podejmować decyzje, które mogą umożliwić współistnienie różnych potrzeb biznesowych.

Proces ZBB i „kompromisy” Co-Dev, gdy są podejmowane po podjęciu właściwych decyzji z odpowiednimi danymi wejściowymi od wszystkich deweloperów, powinny posłowić umożliwić implementację tego, czego potrzebujesz, bez konieczności myślenia - W jaki sposób scalę mój kod.

ZBB oznacza budżetowanie zerowe . Mówiąc Co-Dev, miałem na myśli kilka osób, które pracują w programowaniu równoległym.

Yosi Dahari
źródło
2
czym są „ZBB” i „Co-Dev”?
komar
ZBB - en.wikipedia.org/wiki/Zero-based_budgeting . Mówiąc Co-Dev, miałem na myśli kilka osób, które pracują w programowaniu równoległym.
Yosi Dahari,
1

Problem wydaje mi się, że pracujesz zbyt długo na gałęziach. Koszt konfliktów rośnie wykładniczo wraz z długością, kiedy wszyscy pozostają na gałęzi, więc przy bardzo długich konfliktach masz małe szanse na dokonanie refaktoryzacji.

gnasher729
źródło
0

Twoim problemem jest używany model gałęzi. Możesz rozwijać się w gałęzi, a gdy jest ona kompletna i gotowa do kontroli jakości, gałąź zostaje scalona do „pośredniego łącza”, czasami nazywanego Integracją lub Testem. Podczas opracowywania kolejnej funkcji można zamiast tego rozgałęziać się z tego pośredniego pnia.

Ten model umożliwia jednoczesne rozwijanie wielu funkcji w różnych gałęziach, łączenie ich wszystkich razem w gałęzi Integration w celu wysłania do kontroli jakości, a także utrzymanie pojedynczej linii wydań (łączenie otrzymanej bazy kodu z główną magistralą po ich certyfikacji. )

Przyjmujesz założenie, że zmiany wprowadzone do kontroli jakości zostaną przekazane bez większych modyfikacji - jeśli kod kontroli jakości wróci z instrukcjami usuwania połowy zmian, będziesz musiał cofnąć zmiany, ale jeśli tak się nie stanie, nastąpi twój rozwój jest znacznie płynniejszy. Więc w zasadzie odejmujesz gałęzie dla nowych funkcji od tego, jaki będzie twój główny kod (tj. Pień po scaleniu kodu przekazanego do kontroli jakości), zamiast tego, co jest dzisiaj (tj. Bieżący pień), a więc nie rozwijasz się już w stosunku do bazy kodu z poprzedniej wersji .

gbjbaanb
źródło