Jaki jest najlepszy sposób obsługi refaktoryzacji dużego pliku?


41

Obecnie pracuję nad większym projektem, który niestety zawiera niektóre pliki, w których wytyczne dotyczące jakości oprogramowania nie zawsze były przestrzegane. Obejmuje to duże pliki (odczytaj 2000-4000 wierszy), które wyraźnie zawierają wiele różnych funkcji.

Teraz chcę przekształcić te duże pliki w wiele małych. Problem polega na tym, że ponieważ są tak duże, wiele osób (w tym ja) w różnych oddziałach pracuje nad tymi plikami. Więc nie mogę tak naprawdę odejść od rozwoju i refaktoryzacji, ponieważ połączenie tych refaktoryzacji ze zmianami innych ludzi stanie się trudne.

Moglibyśmy oczywiście wymagać od wszystkich scalenia z powrotem w celu opracowania, „zamrożenia” plików (tj. Nie zezwalaj nikomu na ich edycję), refaktoryzacji, a następnie „odblokowania”. Ale to też nie jest naprawdę dobre, ponieważ wymagałoby to od wszystkich, aby po prostu przestali pracować nad tymi plikami, dopóki nie zostanie dokonane refaktoryzacja.

Czy jest więc sposób na refaktoryzację, czy nie wymaga się od nikogo, aby przestał działać (na długo) lub scalił swoje gałęzie funkcji, aby się rozwijać?


6
Myślę, że zależy to również od użytego języka programowania.
Robert Andrzejuk

8
Lubię „małe przyrostowe” zameldowania. O ile ktoś nie aktualizuje kopii repo, ta praktyka zminimalizuje konflikty scalania dla wszystkich.
Matt Raffel

5
Jak wyglądają twoje testy? Jeśli zamierzasz refaktoryzować duży (i prawdopodobnie ważny!) Fragment kodu, upewnij się, że Twój pakiet testowy jest w naprawdę dobrym stanie przed refaktoryzacją. Ułatwi to upewnienie się, że masz to dobrze w mniejszych plikach.
corsiKa

1
Istnieje wiele podejść, które możesz zastosować przy tym, a najlepsze podejście będzie zależeć od twojej sytuacji.
Stephen

3
Dołączyłem do projektu, w którym największy plik ma długość 10 000 linii, zawierający między innymi klasę o długości 6 000 linii i wszyscy boją się go dotykać. Mam na myśli to, że twoje pytanie jest świetne. Wymyśliliśmy nawet żart, że ta pojedyncza klasa jest dobrym powodem do odblokowania kółka przewijania w naszych myszach.
ElmoVanKielmo

Odpowiedzi:


41

Prawidłowo zrozumiałeś, że nie jest to problem techniczny, ale społeczny: jeśli chcesz uniknąć nadmiernych konfliktów scalania, zespół musi współpracować w sposób, który pozwoli uniknąć tych konfliktów.

Jest to część większego problemu z Gitem, ponieważ rozgałęzianie jest bardzo łatwe, ale scalanie może nadal wymagać dużego wysiłku. Zespoły programistów mają tendencję do uruchamiania wielu oddziałów, a następnie są zaskoczeni, że ich połączenie jest trudne, być może dlatego, że próbują naśladować Git Flow bez zrozumienia jego kontekstu.

Ogólna zasada szybkiego i łatwego łączenia polega na zapobieganiu kumulacji dużych różnic, w szczególności że gałęzie cech powinny być bardzo krótkie (godziny lub dni, a nie miesiące). Zespół programistów, który jest w stanie szybko zintegrować swoje zmiany, zobaczy mniej konfliktów scalania. Jeśli jakiś kod nie jest jeszcze gotowy do produkcji, być może uda się go zintegrować, ale dezaktywować za pomocą flagi funkcji. Jak tylko kod zostanie zintegrowany z gałęzią master, staje się dostępny dla rodzaju refaktoryzacji, który próbujesz zrobić.

To może być za dużo dla twojego bezpośredniego problemu. Ale może być możliwe poproszenie współpracowników o scalenie ich zmian, które wpływają na ten plik, do końca tygodnia, aby można było dokonać refaktoryzacji. Jeśli będą czekać dłużej, sami będą musieli poradzić sobie z konfliktami scalania. To nie jest niemożliwe, to po prostu praca, której można uniknąć.

Możesz także chcieć zapobiec łamaniu dużych pokosów zależnego kodu i wprowadzać tylko zmiany kompatybilne z API. Na przykład, jeśli chcesz wyodrębnić niektóre funkcje do oddzielnego modułu:

  1. Wyodrębnij funkcjonalność do osobnego modułu.
  2. Zmień stare funkcje, aby przekazywać wywołania do nowego interfejsu API.
  3. Z czasem kod zależny od portu do nowego interfejsu API.
  4. Na koniec możesz usunąć stare funkcje.
  5. (Powtórz dla następnej grupy funkcji)

Ten wieloetapowy proces pozwala uniknąć wielu konfliktów scalania. W szczególności będą konflikty tylko wtedy, gdy ktoś inny zmieni wyodrębnioną funkcjonalność. Koszt takiego podejścia polega na tym, że jest on znacznie wolniejszy niż zmienianie wszystkiego naraz, i że masz tymczasowo dwa duplikaty interfejsów API. Nie jest tak źle, dopóki coś pilnego nie zakłóci tego refaktoryzacji, duplikacja zostanie zapomniana lub zdeprializowana, a ty skończysz z długiem technologicznym.

Ale ostatecznie każde rozwiązanie wymagać będzie koordynacji z zespołem.


1
@Laiv Niestety jest to bardzo ogólna rada, ale niektóre pomysły ze zwinnej przestrzeni, takie jak ciągła integracja, mają swoje zalety. Zespoły, które pracują razem (i często integrują swoją pracę) będą miały łatwiejszy czas na wprowadzanie dużych zmian przekrojowych niż zespoły, które pracują tylko obok siebie. Niekoniecznie chodzi o SDLC w ogóle, więcej o współpracy w zespole. Niektóre podejścia sprawiają, że praca obok staje się bardziej wykonalna (myśl Zasada Otwarta / Zamknięta, mikrousługi), ale zespołu OP jeszcze nie ma.
amon

22
Nie posunąłbym się nawet do stwierdzenia, że ​​gałąź cech musi mieć krótki okres użytkowania - po prostu, że nie powinna odbiegać od gałęzi macierzystej przez długi czas. Regularne łączenie zmian z gałęzi nadrzędnej do gałęzi funkcji działa w tych przypadkach, w których gałąź funkcji musi pozostać dłużej. Mimo to dobrym pomysłem jest utrzymywanie gałęzi funkcji nie dłużej niż to konieczne.
Dan Lyons

1
@Laiv Z mojego doświadczenia wynika, że ​​warto wcześniej omówić z zespołem projekt dotyczący post refaktoryzacji, ale zwykle najłatwiej jest, jeśli jedna osoba wprowadza zmiany w kodzie. W przeciwnym razie powrócisz do problemu, że musisz scalić różne rzeczy. Linie 4k brzmią dużo, ale tak naprawdę nie są przeznaczone do ukierunkowanych refaktoryzacji, takich jak klasa ekstraktów . (Tak bardzo bym tu sfilmował książkę Martina Fowlera o refaktoryzacji, gdybym ją przeczytał.) Ale 4k wierszy to dużo tylko dla niedokierowanych refaktoryzacji, takich jak „zobaczmy, jak mogę to poprawić”.
amon

1
@DanLyons Zasadniczo masz rację: może to rozłożyć część wysiłków związanych z łączeniem. W praktyce scalanie Gita zależy w dużej mierze od ostatniego wspólnego zatwierdzenia przodków dla łączonych gałęzi. Scalanie wzorca → funkcja nie daje nam nowego wspólnego przodka nad wzorcem, ale scalanie funkcji → wzorzec daje. Przy wielokrotnym łączeniu funkcji master → może się zdarzyć, że będziemy musieli rozwiązywać te same konflikty raz za razem (ale zobacz git rerere, aby to zautomatyzować). Rebasing jest tutaj absolutnie lepszy, ponieważ końcówka mistrza staje się nowym wspólnym przodkiem, ale przepisywanie historii ma inne problemy.
amon

1
Odpowiedź jest dla mnie w porządku, z wyjątkiem obawy, że git sprawia, że ​​zbyt łatwo jest rozgałęzić się, a zatem deweloperzy zbyt często rozgałęziają się. Dobrze pamiętam czasy SVN, a nawet CVS, kiedy rozgałęzianie było trudne (a przynajmniej kłopotliwe) na tyle, że ludzie na ogół unikali go, jeśli to możliwe, ze wszystkimi powiązanymi problemami. W git, będąc systemem rozproszonym , posiadanie wielu gałęzi tak naprawdę nie różni się niczym od posiadania wielu oddzielnych repozytoriów (tj. Dla każdego dewelopera). Rozwiązanie leży gdzie indziej, łatwość rozgałęzienia nie stanowi problemu. (I tak, widzę, że to tylko na bok ... ale nadal).
AnoE

30

Wykonaj refaktoryzację w mniejszych krokach. Powiedzmy, że twój duży plik ma nazwę Foo:

  1. Dodaj nowy pusty plik Bari przypisz go do „trunk”.

  2. Znajdź niewielką część kodu, w Fooktórej można przenieść Bar. Zastosuj przeniesienie, zaktualizuj z pnia, skompiluj i przetestuj kod i zatwierdz do „pnia”.

  3. Powtórz krok 2 aż Fooi Barmają jednakową wielkość (lub niezależnie od wielkości wolisz)

W ten sposób, gdy następnym razem twoi koledzy z drużyny zaktualizują swoje gałęzie z pnia, otrzymają twoje zmiany w „małych porcjach” i będą mogli łączyć je jeden po drugim, co jest o wiele łatwiejsze niż łączenie pełnego podziału w jednym kroku. To samo dotyczy sytuacji, gdy w kroku 2 występuje konflikt scalania, ponieważ ktoś inny zaktualizował połączenie między nimi.

Nie wyeliminuje to konfliktów scalania ani konieczności ich ręcznego rozwiązywania, ale ogranicza każdy konflikt do niewielkiego obszaru kodu, który jest o wiele łatwiejszy do zarządzania.

I oczywiście - poinformuj o refaktoryzacji w zespole. Poinformuj swoich partnerów o tym, co robisz, aby wiedzieli, dlaczego muszą oczekiwać konfliktów scalania dla konkretnego pliku.


2
Jest to szczególnie przydatne przy włączonej rerereopcji gits
D. Ben Knoble

@ D.BenKnoble: dzięki za ten dodatek. Muszę przyznać, że nie jestem ekspertem od git (ale opisany problem nie dotyczy konkretnie git, dotyczy on każdego VCS, który umożliwia rozgałęzianie, a moja odpowiedź powinna pasować do większości tych systemów).
Doc Brown

Myślałem na podstawie terminologii; w rzeczywistości w przypadku git ten rodzaj scalania jest nadal wykonywany tylko raz (jeśli ktoś po prostu ściąga i łączy). Ale zawsze można ciągnąć i wybierać, łączyć poszczególne zatwierdzenia lub dokonywać zmian w zależności od preferencji twórcy. Zajmuje to więcej czasu, ale z pewnością jest wykonalne, jeśli automatyczne łączenie wydaje się nie powieść.
D. Ben Knoble

18

Zastanawiasz się nad podzieleniem pliku jako operacji atomowej, ale możesz wprowadzić zmiany pośrednie. Plik stopniowo z czasem stał się ogromny, z czasem może stać się mały.

Wybierz część, która od dawna nie musiała się zmieniać ( git blamemoże ci w tym pomóc) i najpierw ją rozdziel. Połącz tę zmianę z gałęziami wszystkich, a następnie wybierz następną najłatwiejszą część do podzielenia. Być może nawet podzielenie jednej części jest zbyt dużym krokiem i najpierw powinieneś po prostu przeorganizować duży plik.

Jeśli ludzie często nie łączą się z powrotem w celu rozwoju, powinieneś zachęcić to, a następnie po scaleniu skorzystaj z okazji, aby oddzielić części, które właśnie zmienili. Lub poproś ich o dokonanie podziału w ramach przeglądu żądania ściągnięcia.

Chodzi o to, aby powoli zmierzać do celu. Będzie się wydawało, że postęp jest powolny, ale nagle zdasz sobie sprawę, że Twój kod jest znacznie lepszy. Przekształcenie liniowca oceanicznego zajmuje dużo czasu.


Plik mógł być duży. Pliki tego rozmiaru można szybko utworzyć. Znam ludzi, którzy potrafią napisać 1000 LoC w ciągu dnia lub tygodnia. A OP nie wspomniał o testach automatycznych, co wskazuje mi, że ich brakuje.
ChuckCottrill

9

Mam zamiar zaproponować inne niż normalne rozwiązanie tego problemu.

Użyj tego jako zdarzenia kodu zespołu. Niech wszyscy sprawdzą swój kod, kto może, a następnie pomogą innym, którzy nadal pracują z plikiem. Po tym, jak wszyscy zainteresowani sprawdzą swój kod, znajdź salę konferencyjną z projektorem i pracuj razem, aby rozpocząć przenoszenie rzeczy do nowych plików.

Być może zechcesz ustawić na to określoną ilość czasu, aby nie skończyło się to na kłótniach trwających tydzień, bez końca. Zamiast tego może to być nawet cotygodniowe wydarzenie trwające 1-2 godziny, dopóki wszyscy nie zaczną wyglądać tak, jak trzeba. Być może potrzebujesz tylko 1-2 godzin na refaktoryzację pliku. Prawdopodobnie nie będziesz wiedział, dopóki nie spróbujesz.

Ma to tę zaletę, że każdy znajduje się na tej samej stronie (bez zamierzonej gry słów) przy refaktoryzacji, ale może również pomóc w uniknięciu błędów, a także uzyskać informacje od innych na temat możliwych grup metod, które należy zachować, jeśli to konieczne.

Robiąc to w ten sposób można uznać, że ma wbudowany przegląd kodu, jeśli robisz coś takiego. Dzięki temu odpowiednia liczba programistów może wypisać się z Twojego kodu, gdy tylko go zalogujesz i będziesz gotowy do sprawdzenia. Nadal możesz chcieć, aby sprawdzili kod pod kątem wszystkiego, co przegapiłeś, ale jest wiele sposobów, aby upewnić się, że proces recenzji jest krótszy.

Może to nie działać we wszystkich sytuacjach, zespołach lub firmach, ponieważ praca nie jest dystrybuowana w sposób, który ułatwia to. Może to być również (niepoprawnie) interpretowane jako niewłaściwe wykorzystanie czasu twórczego. Ten kod grupy wymaga wpisu od menedżera, a także od samego refaktora.

Aby pomóc w sprzedaży tego pomysłu swojemu menedżerowi, wspomnij o kodzie przeglądu kodu, a także o tym, że wszyscy wiedzą od początku. Zapobieganie stracie czasu przez deweloperów podczas przeszukiwania wielu nowych plików może być warte uniknięcia. Również zapobieganie otrzymywaniu przez deweloperów informacji o tym, gdzie skończyło się coś lub „całkowicie brakuje”, jest zwykle dobrą rzeczą. (Im mniej krachów, tym lepiej, IMO.)

Po dokonaniu refaktoryzacji jednego pliku w ten sposób możesz łatwiej uzyskać zgodę na utworzenie większej liczby refaktorów, jeśli byłby on skuteczny i użyteczny.

Jakkolwiek zdecydujesz się na swój refaktor, powodzenia!


To fantastyczna sugestia, która zawiera naprawdę dobry sposób na koordynację zespołu, który będzie miał kluczowe znaczenie dla jego działania. Ponadto, jeśli niektórych gałęzi nie można połączyć z powrotem do masterpierwszego, przynajmniej masz wszystkich w pokoju, którzy pomogą ci poradzić sobie z połączeniami w tych gałęziach.
Colin Young,

+1 za zasugerowanie moba kodowego
Jon Raynor

1
To dokładnie odnosi się do społecznego aspektu problemu.
ChuckCottrill

4

Rozwiązanie tego problemu wymaga wpisu od innych zespołów, ponieważ próbujesz zmienić udostępniony zasób (sam kod). Biorąc to pod uwagę, myślę, że istnieje sposób na „migrację” z posiadania ogromnych monolitycznych plików bez zakłócania spokoju ludzi.

Radziłbym również, aby nie celować jednocześnie w wszystkie ogromne pliki, chyba że liczba dużych plików rośnie w sposób niekontrolowany oprócz rozmiarów poszczególnych plików.

Refaktoryzacja dużych plików w ten sposób często powoduje nieoczekiwane problemy. Pierwszym krokiem jest powstrzymanie dużych plików przed gromadzeniem dodatkowej funkcjonalności wykraczającej poza to, co obecnie znajduje się w gałęzi master lub programistycznej .

Myślę, że najlepszym sposobem na to jest przechwytywanie zatwierdzeń, które domyślnie blokują pewne dodatki do dużych plików, ale można je zastąpić magicznym komentarzem w komunikacie zatwierdzenia, jak @bigfileoki coś takiego. Ważne jest, aby móc unieważnić polisę w sposób bezbolesny, ale możliwy do śledzenia. Idealnie, powinieneś być w stanie uruchomić hak zatwierdzania lokalnie i powinien on powiedzieć, jak zastąpić ten konkretny błąd w samym komunikacie o błędzie . Jest to również moja preferencja, ale nierozpoznane magiczne komentarze lub magiczne komentarze tłumiące błędy, które nie zostały uruchomione w komunikacie zatwierdzenia, powinny być ostrzeżeniem lub błędem czasu zatwierdzenia, abyś nie szkolił ludzi przez pomijanie haków niezależnie od czy tego potrzebują, czy nie.

Hak zatwierdzania może sprawdzać nowe klasy lub przeprowadzać inne analizy statyczne (ad hoc lub nie). Możesz także wybrać liczbę wierszy lub znaków, która jest o 10% większa niż obecnie plik i powiedzieć, że duży plik nie może przekroczyć nowego limitu. Możesz także odrzucić pojedyncze zatwierdzenia, które powiększają duży plik o zbyt wiele wierszy lub zbyt wiele znaków lub w / e.

Gdy duży plik przestanie gromadzić nową funkcjonalność, możesz go refaktoryzować pojedynczo (i jednocześnie zmniejszać progi wymuszone przez haki zatwierdzania, aby zapobiec ponownemu wzrostowi).

W końcu duże pliki będą wystarczająco małe, aby haki zatwierdzania mogły zostać całkowicie usunięte.


-3

Poczekaj na czas rodzinny. Podziel plik, zatwierdź i scal do master.

Inne osoby będą musiały wprowadzić zmiany do swoich gałęzi funkcji rano jak każda inna zmiana.


3
Wciąż oznaczałoby to, że musieliby połączyć moje refaktoryzacje ze swoimi zmianami ...
Hoff


1
Cóż, w rzeczywistości muszą poradzić sobie z połączeniami, jeśli wszyscy zmieniają te pliki.
Laiv

9
Ma to problem: „Niespodzianka, zepsułem wszystkie twoje rzeczy”. PO musi uzyskać wpis i zgodę przed zrobieniem tego, a zrobienie tego w zaplanowanym czasie, aby nikt inny nie miał pliku „w toku”, pomogłoby.
computercarguy

6
Na miłość cthulhu nie rób tego. To najgorszy sposób na pracę w zespole.
Lekkość ściga się z Monicą
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.