Zmieniłem podpis jednej metody i mam teraz ponad 25 000 błędów. Co teraz?

166

Niedawno rozpocząłem nową pracę, w której pracuję nad bardzo dużą aplikacją (15M loc). W mojej poprzedniej pracy mieliśmy podobnie dużą aplikację, ale (na lepsze lub gorsze) korzystaliśmy z OSGi, co oznaczało, że aplikacja została podzielona na wiele mikrousług, które można niezależnie zmieniać, kompilować i wdrażać. Nowa aplikacja to tylko jedna duża baza kodu, może z kilkoma bibliotekami .dll.

Muszę więc zmienić interfejs tej klasy, ponieważ o to poprosił mój szef. Początkowo napisali to z pewnymi założeniami, które nie uogólniły się zbyt dobrze, i przez pewien czas unikali problemu refaktoryzacji, ponieważ jest tak ściśle powiązany. Zmieniłem interfejs i teraz jest ponad 25 000 błędów. Niektóre błędy występują w klasach z ważnymi brzmiącymi nazwami, takimi jak „XYZPriceCalculator”, które w rzeczywistościnie powinien się złamać. Ale nie mogę uruchomić aplikacji, aby sprawdzić, czy działa, dopóki wszystkie błędy nie zostaną usunięte. I wiele testów jednostkowych albo bezpośrednio odwołuje się do tego interfejsu, albo jest sprzężonych z klasami bazowymi, które odwołują się do tego interfejsu, więc samo ich naprawienie jest bardzo dużym zadaniem. Poza tym tak naprawdę nie wiem, jak te wszystkie elementy pasują do siebie, więc nawet gdybym mógł to zacząć, tak naprawdę nie wiem, jak by to wyglądało, gdyby coś się zepsuło.

Nigdy tak naprawdę nie spotkałem się z takim problemem podczas mojej ostatniej pracy. Co ja robię?

użytkownik788497
źródło
7
Będziesz musiał podać nam więcej informacji na temat rodzajów błędów i tego, czym jest „ta klasa”, nie mamy na myśli czytelników.
whatsisname
137
„ponad 25000 błędów”, takie liczby pokazane w VS są często błędne. Istnieje kilka błędów, ale z uszkodzoną kompilacją często używanego dll, inne kompilacje również zawodzą, powodując wzrost liczby błędów astronomicznych. Zawsze zaczynam naprawianie od pierwszego komunikatu o błędzie znalezionego w danych wyjściowych kompilacji, a nie na liście błędów.
Bernhard Hiller,
13
Chciałbym zobaczyć podpisy przed i po metodzie, którą zmieniłeś. BTW - błędy 25k nie brzmią jak zbyt wiele do rozwiązania. Nużące tak, zniechęcające, tak, niemożliwe do opanowania, nie.
jmoreno
46
Interfejs publiczny to umowa. Nie zrywaj takich umów - twórz nowe.
Mateusz
6
25000 błędów !? Houston, mamy problem. Zdecydowanie cofnij zmianę i porozmawiaj ze swoim menedżerem, wyjaśnij mu, że być może będziesz musiał całkowicie stworzyć nowy interfejs.
Code Whisperer

Odpowiedzi:

349

25000 błędów w zasadzie oznacza „nie dotykaj tego”. Zmień to z powrotem. Utwórz nową klasę z pożądanym interfejsem i powoli przenieś konsumentów tej klasy do nowej. W zależności od języka możesz oznaczyć starą klasę jako przestarzałą, co może powodować różnego rodzaju ostrzeżenia kompilatora, ale w rzeczywistości nie spowoduje uszkodzenia kompilacji.

Niestety takie rzeczy zdarzają się w starszych bazach kodu. Niewiele można na to poradzić, z wyjątkiem powolnego ulepszania. Kiedy tworzysz nowe klasy, upewnij się, że odpowiednio je testujesz i tworzysz je zgodnie z zasadami SOLID, aby łatwiej było je zmienić w przyszłości.

Stephen
źródło
79
To niekoniecznie musi być nowa klasa . W zależności od języka i okoliczności może to być funkcja, interfejs, cecha itp.
Jan Hudec,
33
Pomaga również, jeśli stary interfejs można zastąpić opakowaniem zgodności wokół nowego.
Jan Hudec
79
Czasami 25000 błędów w rzeczywistości oznacza, że nigdy nie odważyliśmy się tego dotknąć, ale teraz, gdy przybył przybysz, powierzmy mu zadanie oczyszczenia stajni Augeana.
mouviciel
13
@ thehemi: Zgadzam się, 1 zmiana i błędy 25k najprawdopodobniej oznaczają co najwyżej 2,5k zmian i nie zdziwiłbym się, gdyby było około 250. Dużo pracy w obu kierunkach, ale wykonalne.
jmoreno
33
Te 25000 błędów można naprawić pojedynczą zmianą. Jeśli zniszczę klasę podstawową ogromnej heirarchii, każda z klas pochodnych wyrzuci błędy dotyczące nieprawidłowej podstawy, każde użycie tych klas wyrzuci błędy dotyczące klasy nieistniejącej itp. Wyobraź sobie, że to było „getName”, a ja dodałem argument. Zastąpienie w klasie implementacji „HasAName” nie działa teraz (błąd), a wszystko , co po niej dziedziczy, generuje błędy (wszystkie 1000 klas), a także za każdym razem, gdy tworzę instancję dowolnej z nich (24x na klasę na średni). Poprawka to ... jedna linia.
Jak
80

Dziel i podbijaj za pomocą refaktoryzacji

Często podzielenie zmiany, którą musisz zrobić, na mniejsze kroki może pomóc, ponieważ możesz wtedy wykonać większość mniejszych kroków w sposób, który wcale nie psuje oprogramowania. Narzędzia do refaktoryzacji bardzo pomagają w takich zadaniach.

Podzielić

Najpierw zidentyfikuj najmniejsze możliwe zmiany (pod względem zmian logicznych, a nie pod względem zmienionego LoC), które sumują się do zmiany, którą chcesz osiągnąć. Szczególnie staraj się wyodrębnić etapy, które są czystymi refaktoryzacjami i mogą być wykonane za pomocą narzędzi.

Podbić

W przypadku skomplikowanych przypadków, takich jak Twoja, sensowne może być wykonanie jednego małego refaktoryzacji na raz, a następnie pozostawienie problemu na odpoczynek, aby wszystkie systemy ciągłej integracji mogły zweryfikować zmianę, a być może zespół testowy również to obejrzał. To potwierdza wykonane kroki.

Aby przeprowadzić konkretną refaktoryzację, absolutnie potrzebujesz wsparcia narzędziowego w przypadku, gdy masz 25 000 stron wywoławczych metody, którą chcesz zmienić. Być może działa również funkcja wyszukiwania i zamiany, ale w tak krytycznym przypadku byłbym wystraszony.

Przykład

Na przykład w języku C # można użyć Resharper do zmiany podpisu metody. Jeśli zmiana jest wystarczająco prosta, np. Dodanie nowego parametru, możesz określić, która wartość powinna być używana w witrynach wywoławczych, które w przeciwnym razie miałyby błąd kompilacji.

Jesteś wtedy bezbłędnie w bazie kodu i możesz uruchomić wszystkie testy jednostkowe, które przejdą, ponieważ była to tylko refaktoryzacja.

Gdy podpis metody będzie wyglądał dobrze, możesz przejść i zastąpić wartości dodane przez Resharper jako argumenty do nowo wprowadzonego parametru. Nie jest to już refaktoryzacja , ale masz bazę kodu wolną od błędów i możesz uruchamiać testy po każdej zmianie wiersza.

Czasami to nie działa

Jest to bardzo przydatne podejście w przypadkach takich jak Twoja, w których jest wiele witryn z połączeniami telefonicznymi. I masz już działające oprogramowanie, więc może być możliwe wykonanie małych kroków refaktoryzacyjnych, aby nieco zmienić sygnaturę, a następnie zrobić kolejny.

Niestety nie zadziała, jeśli zmiana podpisu jest zbyt złożona i nie można jej podzielić na mniejsze zmiany. Ale to rzadkie; podział problemu na mniejsze problemy zwykle pokazuje, że jest to możliwe.

theDmi
źródło
12
To. Refaktoryzuj, jeśli możesz (bezpiecznie), w przeciwnym razie potrzebujesz odpowiedniej migracji.
śleske,
3
I na miłość cokolwiek, proszę skorzystać z wbudowanych narzędzi refaktoryzacyjnych IDE, zamiast po prostu lokalnie zmienić (np.) Sygnaturę metody, a następnie przejść dookoła, naprawiając wynikające z tego błędy.
KlaymenDK,
36

Wyjaśnij swoje zadanie swojemu szefowi, aby pomóc mu zrozumieć problem i twoje potrzeby jako profesjonalnego programisty.

Jeśli należysz do zespołu, poszukaj głównego programisty i poproś go o radę.

Powodzenia.

mmehl
źródło
15
To pierwszy krok, który zrobię - „Jestem młodszy, a mój szef kazał mi coś zrobić, a teraz otrzymuję naprawdę duży komunikat o błędzie, ale mój szef nie powiedział mi o tym”. Krok 1: „Hej szefie, ja to zrobiłem, ale teraz dostaję 25 000 błędów. Czy to ma się zdarzyć, czy ...”
Pimgd,
28

Nie dotykaj tego. Nic nie popełniaj.

Zamiast tego usiądź na krześle i wykrzycz „Heeeeelp !!!!!” tak głośno, jak tylko możesz.

Cóż, niezupełnie tak, ale poproś o radę któregokolwiek z twoich starszych kolegów. Jeśli masz 25 000 błędów, nie naprawiasz błędów, naprawiasz to, co je spowodowało. Starszy kolega powinien być w stanie doradzić, jak wprowadzić zmiany, których chce szef bez 25 000 błędów. Można to zrobić na różne sposoby, ale dobry sposób zależy od konkretnej sytuacji.

I może to być szef powiedział twoim starszym kolegom, aby dokonali tej samej zmiany, a oni powiedzieli „nie”. Ponieważ wiedzieli, co się stanie. Właśnie dlatego dostałeś pracę.

gnasher729
źródło
2
Jest to zdecydowanie ważna rzecz, o której należy pamiętać. Jeśli nowy pracownik jest naprawdę ambitny w dużym stopniu, może być wystarczająco pracowity (łatwowierny), aby wykonać naprawdę duże zadanie, które kończy się na zdobyciu 5-bajtowej przewagi pamięciowej lub może być nieco bardziej logiczne, aby później go przekodować i utrzymać.
The Great Duck
@TheGreatDuck: Powiedz mi, że nigdy nie widziałeś interfejsu używanego wszędzie i wszędzie źle . Na pewno mam.
Joshua,
@Joshua, to nawet nie ma związku z tym, co właśnie powiedziałem. Czasami naprawianie drobnego błędu nie zawsze jest najmądrzejszym pomysłem, ponieważ przepisuje ponad 10 000 linii kodu. Niestety, nowy pracownik może być na tyle łatwowierny, że wyciągnie z niego 10 całonocnych pracowników, którzy przepisują ten kod, by zrobić wrażenie na szefie i załatwić sprawę. Jasne, to świetna rzecz, ale czasem wystarczy. Musisz tylko zaakceptować istnienie błędu.
The Great Duck
1
@Joshua btw, dołączyłem tylko do tej społeczności, ponieważ to pytanie pojawiło się na moim popularnym kanale pytań. Nie mam doświadczenia w projektowaniu na dużą skalę. Właśnie zgodziłem się z poglądem tej osoby.
The Great Duck
22

Osadzonych interfejsów API nie można po prostu zmienić. Jeśli naprawdę chcesz je zmienić, zrób anotację i / lub udokumentuj jako przestarzałe (bez względu na to, na co pozwala język) i udokumentuj, którego interfejsu API należy użyć zamiast tego. Stare API można następnie stopniowo wycofywać ... być może bardzo powoli, w zależności od budżetu czasu na refaktoryzację.

Kevin Krumwiede
źródło
10

Oceniać

Oceń, czy ta zmiana jest konieczna, czy możesz dodać nową metodę, a drugą zastąpić.

Naprzód

Jeśli zmiana jest konieczna; konieczny jest plan migracji.

Pierwszym krokiem jest wprowadzenie nowej metody, a stara metoda masuje argumenty, aby mogła wywołać nową. Może to wymagać zakodowania kilku rzeczy; w porządku.

To jest punkt zatwierdzenia: sprawdź, czy wszystkie testy przeszły, zatwierdzają, wypychają.

Migrować

Zajęta praca polega na migracji wszystkich rozmówców starej metody do nowej. Na szczęście można to zrobić stopniowo dzięki spedytorowi.

Więc śmiało; nie wahaj się korzystać z narzędzi do pomocy ( sedsą najbardziej podstawowe, są inne).

Oznacz starą metodę jako przestarzałą (z nutą przejścia na nową metodę); pomoże ci dostrzec, czy o czymś zapomniałeś i pomoże, jeśli współpracownik wprowadzi wezwanie do starej metody podczas pracy nad tym.

Jest to punkt zatwierdzenia (a może kilka punktów zatwierdzenia): sprawdź, czy wszystkie testy przeszły, zatwierdzają, wypychają.

Usunąć

Po upływie pewnego czasu (może zaledwie jednego dnia) po prostu usuń starą metodę.

Matthieu M.
źródło
4
sedjest prawdopodobnie złym pomysłem ... Lepiej jest coś, co faktycznie „rozumie” język i nie wprowadza niezamierzonych drastycznych zmian.
wizzwizz4,
1
@ wizzwizz4: Niestety, znalazłem bardzo mało narzędzi, które faktycznie rozumieją język wystarczająco dobrze dla C ++; wydaje się, że większość narzędzi pozwala na zmianę nazwy i to wszystko. To prawda, że ​​zmiana nazwy tylko dokładnych wywołań metod (a nie przeciążeń lub niepowiązanych, ale podobnie nazwanych wywołań metod) jest już imponująca, ale nie wystarcza do niczego bardziej skomplikowanego. Przynajmniej potrzebujesz umiejętności (1) tasowania argumentów, (2) zastosowania transformacji do danego argumentu ( .c_str()na przykład wywołania ) i wprowadzenia nowych argumentów. sedtrochę działa, kompilator łapie później swoje problemy.
Matthieu M.,
1
sed(lub ed) może być odpowiednie do tego rodzaju rzeczy - o ile poprawnie przejrzysz różnicę przed zatwierdzeniem.
Toby Speight,
To jest TCRR html, tak? :)
Daniel Springer
8

Jeśli zmiana w sygnaturze metody jest jedynie zmianą nazwy, prostym rozwiązaniem jest użycie narzędzi do zautomatyzowania zmiany w 25 000 klas, które odwołują się do danej metody.

Zakładam, że po prostu edytowałeś kod ręcznie, co spowodowało wszystkie błędy. Zakładam również, że znasz Javę (patrz odniesienie do OSGi), więc na przykład w Eclipse (nie wiem, jakiego środowiska programistycznego używasz, ale inne środowiska mają podobne narzędzia do refaktoryzacji) możesz użyć opcji „Refaktoryzacja -> Zmień nazwę” zaktualizować wszystkie odwołania do metody, co powinno pozostawiać bez błędów.

Jeśli dokonujesz innych zmian w podpisie metody niż zmiana nazwy (zmiana liczby lub typów parametrów), możesz użyć opcji „Refaktoryzacja -> Zmień podpis metody”. Jednak są szanse, że będziesz musiał być bardziej ostrożny, jak sugerują inne odpowiedzi. Ponadto, niezależnie od rodzaju zmiany, może to być całkiem niezłe zadanie, dokonując wszystkich zmian w bazie kodu zajętego.

MikkelRJ
źródło
2
OP w szczególności powiedział, że OSGi było w ich poprzedniej pracy, więc nie ma to tak naprawdę znaczenia.
CVn
3
@ MichaelKjörling Jeśli OP był programistą Java w swojej poprzedniej pracy, istnieje większa niż kiedykolwiek szansa, że ​​jest programistą Java również w tym zadaniu.
Rich
@ MichaelKjörling Chciałem dać konkretną rekomendację, używając Eclipse do refaktoryzacji, ponieważ OP zna Javę. To, czy OP faktycznie korzysta z Java w bieżącym projekcie, jest mniej ważne, ale powinienem wyjaśnić swoją odpowiedź. Dzięki.
MikkelRJ
6

Oto mój wkład.

Niedawno rozpocząłem nową pracę, w której pracuję nad bardzo dużą aplikacją (15 milionów linii kodu).

Prawdopodobnie nie znasz projektu i jego „funkcji”. Przed wpisaniem jednego wiersza kodu ważne jest, aby zapoznać się z projektem. Więc wycofaj zmiany i zacznij od analizy kodu . (Przynajmniej dotknięty)

Zrozumienie istniejącego rozwiązania daje lepszą perspektywę na to, gdzie się pakujesz. Kontekstualizuj rozwiązanie i jego znaczenie.

Jak wskazał @Greg, powinieneś być w stanie przetestować istniejący kod, aby mieć prawidłowe odwołanie do porównania (testy regresji). Twoje rozwiązanie powinno być w stanie generować takie same wyniki jak istniejące. Na tym etapie nie przejmujesz się, czy wyniki są prawidłowe, czy nie . Pierwszym celem jest refaktoryzacja, a nie naprawianie błędów. Jeśli istniejące rozwiązanie mówi „2 + 2 = 42”, Twoje rozwiązanie również powinno. Jeśli nie rzuca wyjątków, twój też nie powinien. Jeśli zwróci wartości zerowe, twoje również powinny zwrócić wartości zerowe. I tak dalej. W przeciwnym razie naruszysz 25 000 wierszy kodu.

Ma to na celu zapewnienie zgodności retro.

Dlaczego? Ponieważ w tej chwili jest to Twoja wyjątkowa gwarancja udanego refaktora.

I wiele testów jednostkowych albo bezpośrednio odwołuje się do tego interfejsu, albo jest sprzężonych z klasami bazowymi, które odwołują się do tego interfejsu.

Pilnie potrzebny jest sposób, aby zagwarantować kompatybilność retro. Oto Twoje pierwsze wyzwanie. Wyizoluj komponent do testów jednostkowych.

Należy pamiętać, że te 25 tys. Linii kodu zostało stworzonych przy założeniu możliwych wyników istniejącego kodu. Jeśli nie złamiesz tej części umowy, jesteś w połowie drogi do ostatecznego rozwiązania. Jeśli tak, to dobrze: niech siła będzie z tobą

Po zaprojektowaniu i wdrożeniu nowego „kontraktu” zastąp go starym. Przestarzałe lub wyjmij.

Zasugerowałem pozostawienie błędów w spokoju, ponieważ refaktoryzacja i naprawianie błędów to różne zadania. Jeśli spróbujesz je posunąć naprzód, możesz zawieść na obu. Możesz myśleć, że znalazłeś błędy, mogą to być jednak „funkcje”. Więc zostaw je w spokoju (na minutę).

Wydaje mi się, że 25 000 linii kodu wystarcza do tego, aby skupić się tylko na jednym zadaniu.

Po wykonaniu pierwszego zadania. Ujawnij te błędy / funkcje swojemu szefowi.

Wreszcie, jak powiedział @Stephen:

Niewiele można na to poradzić, z wyjątkiem powolnego ulepszania. Kiedy tworzysz nowe klasy, upewnij się, że odpowiednio je testujesz i tworzysz je zgodnie z zasadami SOLID, aby łatwiej było je zmienić w przyszłości

Laiv
źródło
5

Sprawdź to.

Wszyscy inni zalecają sposób refaktoryzacji, aby miał niewielki wpływ. Ale przy tak wielu błędach, nawet jeśli uda Ci się zrefaktoryzować przy użyciu zaledwie 10 linii kodu (prawdopodobnie możesz), to wpłynąłeś na 25 000 przepływów kodu , nawet jeśli nie musiałeś ich przepisywać.

Następną rzeczą do zrobienia jest sprawdzenie, czy zestaw testów regresji przeszedł w żywych kolorach. A jeśli nie masz, zrób taki, który będzie. Dodanie kompleksowego zestawu testów regresji do monolitycznego projektu wydaje się nudne, ale jest dobrym sposobem na zwiększenie zaufania do kandydatów do wydania, a także ich zwolnienie szybciej, jeśli pakiet jest dobrze zautomatyzowany.

Greg
źródło