Refactoring - Jak robić i kiedy merge?

Refactoring - Jak robić i kiedy merge?

Wątek przeniesiony 2020-02-28 18:08 z Off-Topic przez Krolik.

KR
Moderator
  • Rejestracja:prawie 21 lat
  • Ostatnio:2 dni
  • Postów:2964
0

Temat trochę pokrewny do poprzedniego o długu technicznym, ale jednak tym razem chodzi mi o coś szerszego.

Powiedzmy, że zespół osiągnął konsensus, że kod to kupa mułu i podjął decyzję: REFAKTORUJEMY. Np. zamieniamy wszędobylski static i singletony na DI.

No i teraz jak to zrobić dobrze?

  1. Bierzemy tego kto zaproponował pomysł refaktoringu, dajemy mu pół roku i biedaczysko poprawia aż będzie dobrze, oczywiście w swojej gałęzi. Jak skończy - robimy wielkie review, dajemy kolejne 3 miesiące na uwzględnienie uwag i naprawienie konfliktów i po kolejnych 3 miesiącach w końcu mamy merge i możemy świętować.

  2. Przerabiamy kod stopniowo - najpierw dodajemy nowa infrastrukturę, potem przerabiamy komponent po komponencie, każdy zespół przerabia swój komponent na nową metodę. Jak już wszystkie komponenty są ok, wywalamy ewentualnie resztki starego kodu, który nie jest już potrzebny i robimy merge.

  3. Robimy jeden komponent, merge, oczekujemy trochę na stabilizację, robimy kolejny, znowu merge itd. W międzyczasie w kodzie musimy utrzymywać infrastrukturę dla obu sposobów - czyli niektóre rzeczy mają statici a niektóre są przeniesione na nowy framework. Możliwe, że po drodze trzeba stworzyć jakieś warstwy kompatybilności. Sprzątamy dopiero na końcu jak wszystkie komponenty są przeniesione.

  4. Inne. Jakie?

DR
Zespół takie decyzje podejmuje co tydzień, niestety biznes nie chce zapłacić :(
KR
Biznes zawsze płaci - tylko czasem nieświadomie, jak 2 dni przed wydaniem produkt się sypie i nikt nie wie dlaczego, bo nie idzie się połapać w 20-letnim spaghetti. :D
DR
To swoją drogą :P
danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
12

Najpierw napiszcie testy! (chyba ze juz macie na wszystko) Najlepiej e2e zeby móc sobie orać kod dowolnie. Potem starcie sie kolejno wydzielać komponenty. Im częściej merge tym lepiej, bo dzięki temu nikt nie będzie "z tyłu"


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:15 dni
2

Nie widziałem, żeby 1 zadziałało (co nie znaczy, że się nie da). 2 daje efekty bardzo późno, więc jak nie ma wsparcia z góry, to pewnie zostanie ubite w pewnym momencie. 3 jest okej, ale łatwo jest w połowie pracy stwierdzić, że już wystarczy i zostają dwa niekompatybilne rozwiązania.

Bez kodu trudno wyczuć, ale zacząłbym od podejścia 3.

MA
  • Rejestracja:prawie 17 lat
  • Ostatnio:6 dni
  • Postów:644
3

1 i 2 nigdy się nie uda na większych projektach. Tylko 3 ale z dodatkiem testów. Więcej można przeczytać w książce "Praca z zastanym kodem. Najlepsze techniki".

Zespoł musi być świadomy, że to jest potrzebne (jak 1 osoba na 10 w zespole widzi problem to dużo nie zdziała w pojedynkę). W refaktoryzacji nie ma pojęcia "koniec", to proces który powinien być codzienną pracą programisty. Testy to bardzo dobre narzędzie do refaktoru, nawet najlepszy kod bez testów to legacy/zastany kod. Jeżeli napiszecie nowy kod który wydaje Wam się lepszy, fajniejszy itp. ale bez testów - to za kilka lat znowu będziecie musieli przechodzić ten sam proces. Testy jednostkowe i ew. integracyjne tylko dla nowego kodu (ale bez rygoru minimalnego pokrycia, to często prowadzi do testów które nic nie testują), dla legacy często nie da się takich napisać więc wystarczą testy end-to-end. Dodatkowo wszystkie narzędzia które pomagają w trzymaniu jakości kodu np. Sonar i mierzeniu tego co zostało poprawione (zespoł jak widzi wykres, że jest mniej duplikacji, mniej staticów itp. to jest bardziej zmotywowany).

KR
O, nie znałem tej książki. Jest wersja po angielsku? To wyślę architektowi w prezencie.
MA
Oryginał jest po angielsku ;)
KR
Tytuł lub autor?
MA
Working Effectively with Legacy Code, autor ten sam - Michael Feathers
neves
  • Rejestracja:ponad 21 lat
  • Ostatnio:38 minut
  • Lokalizacja:Kraków
  • Postów:1114
4

Również głosuję na 3, żyjemy w czasach agilowych, które nastały właśnie dlatego że big bangowe podejście 1) prawie nigdy się nie sprawdzało bez względu czy tyczy się to refaktoringu czy nowego developmentu. Chcemy jak najszybciej uzyskać feedback że nic nie popsuliśmy i że zmierzamy w dobrym kierunku. Kroimy malutki kawałeczek, przepisujemy go tak by testy były dalej zielone, jak nie mamy testów to pierw testy piszemy, wrzucamy na produkcję i zbieramy metryki(validated learning), robimy retrospekcję, jak wszystko śmiga pięknie powtarzamy całą pętle.


edytowany 2x, ostatnio: neves
GS
  • Rejestracja:ponad 8 lat
  • Ostatnio:dzień
  • Postów:1265
1
Markuz napisał(a):

1 i 2 nigdy się nie uda na większych projektach. Tylko 3 ale z dodatkiem testów. Więcej można przeczytać w książce "Praca z zastanym kodem. Najlepsze techniki".

E tam się nie uda...
Ja właśnie sam robię refaktoring typu 1, tylko że nie miałem pół roku tylko jakieś 2 tygodnie (miałem dziś skończyć, oczywiście nie skończyłem :D ). Testów nie ma, chyba że je napiszę, ale nie planuję, bo nigdy nie skończę. Review to też może mi zrobi 1-2 osoby, bo nikt więcej nie rozumie co się tam dzieje. Projekt na kilkadziesiąt klas, dość złożony, dużo wielowątkowości, algorytmiki, konwolucyjnych sieci neuronowych, mieszany kod C++/CUDA, przetwarzanie obrazów. W międzyczasie naprawiam biblioteki zależne i budowanie paczek debianowych :)

Pozostaję optymistą :)

MA
Nie przepadam za takimi rozwiązaniami, zazwyczaj pomagasz tylko sobie (będziesz lepiej znał cały system) a nie zespołowi (o ile masz zespoł), dla osoby z zewnątrz to nadal będzie tak samo niezrozumiały kod (i wprowadzanie poprawek bez testów będzie tak samo bolało) - mam nadzieję, że przynajmniej zwiększysz jego wydajność (o ile to badasz w jakiś sposób). Musiałbym Cię mocno lubić/ufać żeby z miejsca nie odrzucić PR z takim refaktorem ;)
KR
Kilkadziesiąt klas to nie jest duży projekt, chyba że te klasy mają po 10000 linii, ale to wtedy w sumie nawet jakby była jedna, to współczułbym.
GS
@Markuz: owszem, pomagam sobie, bo pewnie tylko ja będę dalej rozwijał ten projekt. Jeśli mój PR nie będzie zaakceptowany, to równie dobrze można zamrozić ten projekt i go dalej nie rozwijać. @Krolik, nie pisałem że projekt jest duży, natomiast jest dość skomplikowany.
BraVolt
  • Rejestracja:prawie 6 lat
  • Ostatnio:prawie 4 lata
  • Lokalizacja:Warszawa
  • Postów:2918
2
Krolik napisał(a):
  1. Bierzemy tego kto zaproponował pomysł refaktoringu, dajemy mu pół roku i biedaczysko poprawia aż będzie dobrze, oczywiście w swojej gałęzi. Jak skończy - robimy wielkie review, dajemy kolejne 3 miesiące na uwzględnienie uwag i naprawienie konfliktów i po kolejnych 3 miesiącach w końcu mamy merge i możemy świętować.

Bardzo optymistyczne założenie o szampanie po roku. Ale bardziej niesamowite byłoby to review półrocznej pracy człowieka posadzonego sam na sam na pół roku z kodem. Oczywiście testów jak nie było wcześniej tak po pół roku też nie będzie.


"Kiedy wiedzieć czy zacząć nauke Springa? bo w czystej Javie to nic ciekawego nie zrobie chyba"
Ein Volk, ein Reich, ein Kwa-Kwa ***** ***
KR
Nie bądź takim pesymistą, testy w projekcie są.
BraVolt
Ale można na nich polegać czy po prostu "są", czytaj "jakieś testy kiedyś napisano i to co pokrywają nie wywalają"? Bo później terminy goniły i już na testy nie było czasu.
BraVolt
Płynna granica pomiędzy pesymistą a realistą :)
KR
Testy są, pokrycie dość dobre, natomiast większy problem jest taki że wykonanie wszystkich zajmuje kilka godzin na klastrze. No i jest niewielki odsetek testów niestabilnych.
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
2
  • Robimy porządne pokrycie testami akceptacyjnymi, integracyjnymi, e2e
  • Wystawiamy jakiś ogólny interfejs modułu, tak żeby pomiędzy sobą moduły w ogóle nie wiedziały o internalsach i komunikowały się tylko i wyłącznie tym abstrakcyjnym interfejsem
  • Przerabiamy ten moduł i podmieniamy

Czyli generalnie opcja 3. Plus jest taki że szybko werfykujesz czy to w ogóle działa. I nie ma też takiego problemu, że dochodzą nowe ficzery i trzeba je klepać 2 razy (raz w nowym raz w starym systemi). Im szybciej wypchniesz coś co działa, tym lepiej.

Ale dużo zależy od tego jak wygląda aktualny stan i co chcecie osiągnąć. Bo może nie warto trzymać się starego interfejsu.?

Opcja 1 czy 2 mają sens jeśli mozecie sobie pozwolić na freeze produktu (bo np. jest tam już tylko maintenance), szczególnie kiedy wiecie ze chcecie to na pewno zaorać i zrobić od nowa. Jeśli da sie coś "uratować" to opcja 3 zawsze będzie lepsza.

Robiłem ostatnio taki "refaktor" gdzie nie dało się uratować nic. Projekt klepany z 10 lat temu, Spring 2, Hibernate w xmlach, a do tego jeszcze jakiś webowy framework który umarł z 5 lat temu i cała logika aplikacji sklejona z tym webowym frameworkiem albo pisane "na piechote". Jakieś oczywistości jak zwrócenie z kontrolera InputStreama pisane ręcznie przez pisanie w pętli do httpservletresponse itd.
Oczywiście monolit więc nawet nie bardzo jak przepisywać coś po kawałku, bo nie da się wydzielić modułu.
W takiej sytuacji po prostu nie da się inaczej niż 1/2.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 2x, ostatnio: Shalom
LukeJL
  • Rejestracja:około 11 lat
  • Ostatnio:minuta
  • Postów:8403
1

Bierzemy tego kto zaproponował pomysł refaktoringu, dajemy mu pół roku i biedaczysko poprawia aż będzie dobrze, oczywiście w swojej gałęzi

Przez pół roku to można przepisać całą aplikację, a nie tylko zrefaktorować...

Dajcie mu pół tygodnia, niech zrefaktoruje moduł, zmerdżuje się, potem następny moduł itp.

Jak skończy - robimy wielkie review, dajemy kolejne 3 miesiące na uwzględnienie uwag i naprawienie konfliktów i po kolejnych 3 miesiącach w końcu mamy merge i możemy świętować.

= Waterfall.

Nie sądzę, żeby to się sprawdziło.

Przerabiamy kod stopniowo - najpierw dodajemy nowa infrastrukturę, potem przerabiamy komponent po komponencie, każdy zespół przerabia swój komponent na nową metodę. Jak już wszystkie komponenty są ok, wywalamy ewentualnie resztki starego kodu, który nie jest już potrzebny i robimy merge.

No, tu już rozsądniej. Chociaż merge ja bym robił jak najszybciej (Robimy jeden komponent, merge, oczekujemy trochę na stabilizację, robimy kolejny, znowu merge), żeby od razu wykryć, czy coś działa, czy nie.

oczekujemy trochę na stabilizację

Napiszcie sobie testy, to wtedy od razu będziecie wiedzieli, czy nie ma jakichś bugów itp.


edytowany 1x, ostatnio: LukeJL
KR
Testy nigdy nie wyłapia wszystkiego, ale oczywiście zgoda - testy są bardzo ważne.
LukeJL
Nie muszą wyłapać wszystkiego, ważne, żeby wyłapały to, czy dana funkcjonalność działa po refaktorze tak samo poprawnie jak przed refaktorem.
KR
Tak, zgoda. Natomiast wyraziłem się trochę nieprecyzyjnie - chodziło mi też o zebranie opinii wśród innych programistów, którzy zetkną się z nowym API. Czyli dajemy szansę nowemu API się trochę ułożyć w projekcie, zanim będzie stosowane wszędzie.
LS
LS
  • Rejestracja:około 9 lat
  • Ostatnio:prawie 5 lat
  • Postów:85
1

Przy refacoringu takich starych kobył mi się sprawdza algorytm postępowania w stylu:

  1. Staram się napisać testy pokrywające wszystkie "odnogi", przypadki refaktorowanego kodu.
  2. Dopiero wtedy siadam do poprawiania.
  3. Na bieżąco sprawdzam/aktualizuje testy.
  4. Przypisuję wyjadaczy projektowych do code review swojego PR (bo czasami wywalasz coś myśląc, że "po co komu to tutaj potrzebne", a gość pracujący 5 lat w projekcie mówi Ci, że jakiś syf, o którym nigdy nie słyszałeś z tego korzysta i bez tego wszystko je**ie).

Dosyć żmudny sposób, ale już się przejechałem na tym, że jak coś się poprawia w projekcie ze sporym długim technicznym, to jest to najrozsądniejszy i najbardziej opłacalny na dłuższą metę sposób.
Chociaż cięższe od samego sposobu jest raczej przekonanie biznesu, że taka robota jest warta zachodu mimo, że nie widzą na oczy żadnych efektów i nie dowozimy im nowych, bajeranckich ficzerów. :D

edytowany 1x, ostatnio: LowSkiller
IK
Chociaż cięższe od samego sposobu jest raczej przekonanie biznesu, że taka robota jest warta zachodu https://www.youtube.com/watch?v=3eCSYexlf8M&feature=youtu.be&t=1789
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
2

Do mniejszego refactoringu najlepiej wprowadzić w życie Boy Scout Rule (lub zależnie od poglądów ideologicznych po prostu Scout Rule). Innymi słowy- poprawiamy kod w ramach codziennej pracy nad taskami.

Jeśli chodzi o większe zmiany to nie obejdzie się bez wkładu/akceptacji biznesu, a szczegóły naprawdę zależą od natury zmian, projektu i kultury pracy. Moim zdaniem najzdrowszym podejściem jest zaangażowanie większości programistów w projekcie. Jeśli w projekcie używa się np. Scrum to poświęcamy całe iteration na refactoring- żadnych nowych funkcjonalności, wszystkie taski napisane pod poprawę istniejącego kodu/architektury.

Obecnie u mnie w pracy będzie to miało miejsce więc zapowiada się ciekawie :)


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Charles_Ray
Dokładnie, 20% pracy zrobi 80% celu
AN
  • Rejestracja:około 5 lat
  • Ostatnio:prawie 5 lat
  • Postów:3
2

Na temat refactoringu, architektury, testowania przede wszystkim trzeba mieć pojęcie. Jeśli piszesz, że chcecie wymienić wszędobylski static i singletoy na DI to nie brzmi to dobrze. Jeśli sami stworzyliście tego potwora i teraz sami, chcecie to zrefactoryzować, to raczej to nie wypali. Widziałem już jak w jednej firmie, senior zrobił refactoring, z lazanii na inny rodzaj spaghetti. Dobry kod przede wszystkim dobrze się testuje, a na refactoring są też są przydatne pateny np open host, anti corruption layer.

Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
2

@Anonnas: to że wcześniej popełnili szereg błędów (skąd pewności że to oni?) nie znaczy że nie mogą tego zmienić, tym bardziej jeśli zdają sobie sprawę z tychże błędów. Co za tym idzie, jeśli sami stworzyli "tego potwora" nie zmienia faktu że od tamtego czasu mogli sporo się nauczyć oraz mieć nowe, bardziej obeznane osoby w zespole. Ponadto rzucasz ogólniki typu open-closed principle (domyślam się że to miałeś na myśli pisząc open host) oraz ACL jakby to miało się czemuś przysłużyć. O ile open-closed jest jeszcze dosyć uniwersalne, to już ACL jest specyficznym podejściem do konkretnej natury problemu, i jeśli rzucasz ACL za każdym razem kiedy słyszysz o refactoringu to lepiej nie radzić nic w ogóle.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)