Znalazłeś trochę kodu, który wygląda na zbyteczny, a kompilator tego nie zauważa. Co robisz, aby mieć pewność (lub jak najbardziej pewne), że usunięcie tego kodu nie spowoduje regresji.
Przychodzą mi na myśl dwa pomysły.
„Po prostu” użyj dedukcji na podstawie tego, czy kod wygląda na to, że powinien zostać wykonany. Czasami jednak może to być złożona, czasochłonna praca, nieco ryzykowna (twoja ocena jest podatna na błędy), ponieważ nie przynosi istotnych zysków biznesowych.
Umieść logowanie w tej sekcji kodu i zobacz, jak często jest wprowadzane w praktyce. Po wystarczającej liczbie wykonań powinieneś mieć pewność, że usunięcie kodu jest bezpieczne.
Czy są jakieś lepsze pomysły lub coś w rodzaju podejścia kanonicznego?
clean-code
Brad Thomas
źródło
źródło
Odpowiedzi:
W moim idealnym świecie fantasy, w którym mam 100% pokrycia testem jednostkowym, po prostu go usunę, uruchomię testy jednostkowe, a gdy żaden test nie zmieni koloru na czerwony, to go popełniam.
Ale niestety muszę budzić się każdego ranka i stawić czoła trudnej rzeczywistości, w której wiele kodu albo nie ma testów jednostkowych, albo kiedy już tam są, nie można ufać, że naprawdę obejmie każdy możliwy przypadek krawędzi. Rozważę więc ryzyko / nagrodę i dojdę do wniosku, że po prostu nie warto:
źródło
Proces ten składa się z dwóch połówek. Pierwszy potwierdza, że kod jest rzeczywiście martwy. Drugim jest zrozumienie kosztów popełnienia błędu i upewnienie się, że są one odpowiednio złagodzone.
Wiele odpowiedzi tutaj ma świetne rozwiązania dla pierwszej połowy. Narzędzia takie jak analizatory statyczne doskonale nadają się do identyfikowania martwego kodu.
grep
w niektórych przypadkach może być twoim przyjacielem. Jednym z nietypowych kroków, które często podejmuję, jest próba ustalenia pierwotnego celu kodu. O wiele łatwiej jest argumentować, że „X nie jest już cechą naszego produktu, a segment kodu Y został zaprojektowany do obsługi funkcji X”, niż mówiąc „Nie widzę żadnego celu dla segmentu kodu Y”.Druga połowa jest kluczowym krokiem do złamania jakiegokolwiek impasu w kwestii tego, czy powinieneś usunąć kod. Musisz zrozumieć, jakie są konsekwencje błędnej odpowiedzi. Jeśli ludzie umrą, jeśli źle odpowiesz, zwróć uwagę! Może to dobry moment, aby zaakceptować rozwój cruft kodu i starać się nie pisać więcej cruft. Jeśli ludzie nie umrą, zadaj sobie pytanie, jak wybaczają Twoi użytkownicy. Czy możesz wysłać im poprawkę, jeśli coś zepsułeś i utrzymałeś relacje z klientami? Czy masz zespół Q&A, który otrzymuje wynagrodzenie za znalezienie takich problemów? Tego rodzaju pytania są niezbędne, aby zrozumieć, jak pewna musisz być, zanim naciśniesz klawisz usuwania.
W komentarzach rmun wskazał na doskonałe sformułowanie pojęcia zrozumienia pierwotnego celu kodu przed jego usunięciem. Ten cytat jest teraz znany jako Ogrodzenie Chestertona . Chociaż jest on zbyt duży, aby można go było cytować bezpośrednio w komentarzu, myślę, że zasługuje na to, aby go tutaj odpowiednio podać:
źródło
Mam też tendencję do szukania
grep
nazwy funkcji / klasy w kodzie, która może dać dodatkowe korzyści, których nie może analizator kodu, na przykład jeśli nazwa jest wymieniona w komentarzu, pliku dokumentacji lub skrypcie. Uruchamiam grep na plikach w drzewie źródłowym i zapisuję wynik w pliku; zwykle wynik zawiera skondensowane informacje: nazwę pliku / ścieżkę, numer linii i linię, w której napotyka się nazwę, co może dać wskazówki, gdzie funkcja / klasa jest wywoływana lub wymieniana bez żadnego znaczenia semantycznego (w przeciwieństwie do analizatora kodu ) i niezależnie od rozszerzeń plików. Zdecydowanie nie jest to najlepsze rozwiązanie, ale miły dodatek do analizy.źródło
$object->$variableMethod($arg1, $arg2);
grep
np. funkcja obejmująca sekcję kodu może dać wskazówki, w jaki sposób funkcja jest używana, a zatem jej zawartość?źródło
Oprócz wspomnianych istniejących odpowiedzi można również iteracyjnie usunąć kod w kilku wersjach.
W początkowej wersji można było zrobić ostrzeżenie o wycofaniu z działającym blokiem kodu. W późniejszej wersji można było usunąć blok kodu, ale pozostawić komunikat o błędzie, informujący użytkowników, że funkcja jest przestarzała i nie jest już dostępna. W ostatecznej wersji usunąłbyś blok kodu i wszelkie wiadomości.
Może to pomóc zidentyfikować nieprzewidzianą funkcjonalność bez ostrzeżenia dla użytkowników końcowych. W najlepszym przypadku kod naprawdę nic nie robi, a wszystko to dzieje się tak, że niepotrzebny kod jest przechowywany w kilku wersjach przed usunięciem.
źródło
Wiele osób sugeruje, że „bezpieczną” rzeczą jest pozostawienie kodu, jeśli nie możesz udowodnić, że jest nieużywany.
Ale kod nie jest zasobem, ale zobowiązaniem.
O ile nie możesz wyjaśnić, dlaczego jest to ważne i wskazać na jego testy, „bezpieczniejszą” alternatywą może być usunięcie go.
Jeśli nadal nie masz pewności, dodaj przynajmniej testy, aby wykonać kod zombie.
źródło
Możesz użyć przełączników funkcji, aby zmienić ścieżkę wykonania oprogramowania i całkowicie zignorować dany kod.
W ten sposób możesz bezpiecznie wdrożyć swoją zmianę, gdy kod nie jest używany, a przełącznik jest wyłączony. Jeśli zauważysz kilka poważnych błędów związanych z kodem, włącz ponownie przełącznik i sprawdź możliwą ścieżkę do niego.
Takie podejście powinno dać ci pewność, jeśli nie zauważysz żadnych problemów przez dłuższy czas, a także możliwość przywrócenia go bez wdrożenia. Jednak jeszcze lepszym sposobem byłoby zastosowanie dodatkowego rejestrowania i pokrycia testowego na danym obszarze, co dostarczy więcej dowodów na to, czy jest on używany, czy nie.
Przeczytaj więcej o przełącznikach tutaj: https://martinfowler.com/articles/feature-toggles.html
źródło
Oczywiście analiza statyczna ... i miło, że nie potrzebujesz żadnych nowych narzędzi; twój kompilator ma wszystkie potrzebne narzędzia do analizy statycznej.
Po prostu zmień nazwę metody (np. Zmień
DoSomething
naDoSomethingX
), a następnie uruchom kompilację.Jeśli kompilacja się powiedzie, metoda oczywiście nie jest przez nic używana. Bezpieczne do usunięcia.
Jeśli kompilacja się nie powiedzie, odizoluj wiersz kodu, który ją wywołuje, i sprawdź, jakie
if
instrukcje otaczają to połączenie, aby ustalić, jak je wywołać. Jeśli nie możesz znaleźć żadnego możliwego przypadku użycia danych, który je wyzwala, możesz go bezpiecznie usunąć.Jeśli naprawdę martwisz się usunięciem, rozważ zachowanie kodu, ale oznaczenie go za pomocą ObsoleteAttribute (lub odpowiednika w twoim języku). Wydaj go w ten sposób dla jednej wersji, a następnie usuń kod, gdy nie zostaną wykryte żadne problemy na wolności.
źródło
źródło
Usuwanie nieosiągalnego kodu
W statycznym typie języka statycznego zawsze powinieneś wiedzieć, czy kod jest rzeczywiście osiągalny, czy nie: usuń go, skompiluj, jeśli nie ma błędu, to nie był osiągalny.
Niestety, nie wszystkie języki są typowane statycznie i nie wszystkie języki typowane statycznie są oparte na zasadach. Do rzeczy, które mogą pójść nie tak, należą (1) refleksja i (2) nieokreślone przeciążenie.
Jeśli używasz języka dynamicznego lub języka z wystarczająco silnym odzwierciedleniem, że analizowany fragment kodu może być potencjalnie dostępny w czasie wykonywania przez odbicie, nie możesz polegać na kompilatorze. Takie języki to Python, Ruby lub Java.
Jeśli używasz języka z nieprincised przeciążeniem, samo usunięcie przeciążenia może po prostu bez problemu przełączyć rozdzielczość przeciążenia na inne . Niektóre takie języki pozwalają zaprogramować ostrzeżenie / błąd podczas kompilacji związane z użyciem kodu, w przeciwnym razie nie można polegać na kompilatorze. Takie języki to Java (użycie
@Deprecated
) lub C ++ (użycie[[deprecated]]
lub= delete
).Tak więc, chyba że masz szczęście pracować ze ścisłymi językami (przychodzi na myśl Rust), możesz naprawdę strzelać sobie w nogi, ufając kompilatorowi. I niestety pakiety testowe są na ogół niekompletne, więc też niewiele więcej pomocy.
Przejrzyj następną sekcję ...
Usuwanie potencjalnie nieużywanego kodu
Bardziej prawdopodobne jest, że kod jest faktycznie przywoływany, jednak podejrzewasz, że w praktyce gałęzie kodu, które go dotyczą, nigdy nie są pobierane.
W takim przypadku, bez względu na język, kod jest łatwo dostępny i można używać tylko oprzyrządowania w czasie wykonywania.
W przeszłości z powodzeniem stosowałem trójfazowe podejście do usuwania takiego kodu:
Co to jest cykl Jest to cykl użycia kodu. Na przykład w przypadku wniosku finansowego spodziewałbym się krótkiego cyklu miesięcznego (z wypłatami wynagrodzeń na koniec miesiąca) i długiego cyklu rocznego. W takim przypadku musisz poczekać co najmniej rok, aby sprawdzić, czy ostrzeżenie nigdy nie jest emitowane, aby inwentaryzacja na koniec roku mogła korzystać ze ścieżek kodu, które w innym przypadku nigdy nie byłyby używane.
Mamy nadzieję, że większość aplikacji ma krótsze cykle.
Radzę umieścić komentarz do zrobienia TODO z datą, wskazujący, kiedy przejść do następnego kroku. I przypomnienie w twoim kalendarzu.
źródło
[[deprecated]]
aby zidentyfikować strony, które wywołują tę wersję; możesz je sprawdzić, aby sprawdzić, czy ich zachowanie się zmieni. Alternatywnie, zdefiniuj swoje przeciążenie jako= delete
- w takim przypadku będziesz musiał jawnie rzucić argumenty, aby użyć jednej z pozostałych wersji.Usuwanie kodu z produkcji przypomina sprzątanie domu. W momencie, gdy wyrzucisz przedmiot ze strychu, twoja żona zabije cię następnego dnia za wyrzucenie prezentu na powrót siostrzeńca trzeciej pradziadki od sąsiada, który zmarł w 1923 roku.
Poważnie, po pobieżnej analizie przy użyciu różnych narzędzi, o których wszyscy już wspominali, a także po zastosowaniu wspomnianej już metody stopniowej rezygnacji, pamiętaj, że możesz zniknąć z firmy, kiedy nastąpi faktyczne usunięcie. Pozostawienie kodu i zarejestrowanie jego wykonania oraz upewnienie się, że wszelkie powiadomienia o jego wykonaniu zostaną zdecydowanie przekazane Tobie (lub Twoim następcom i cesjonariuszom) jest niezbędne.
Jeśli nie zastosujesz się do tego podejścia, prawdopodobnie zostaniesz zabity przez swoją żonę. Jeśli zachowasz kod (jak każdy pamiątkowy), możesz uspokoić sumienie, że prawo Moore'a przychodzi ci na ratunek, a koszt miejsca na śmieci używanego przez kod, który wydaje się być „kamieniem w moim bucie”, zmniejsza każdy rok i nie ryzykujesz, że staniesz się centrum wielu plotek na temat chłodnicy wodnej i dziwnych spojrzeń na korytarzach.
PS: Aby wyjaśnić w odpowiedzi na komentarz. Pierwotne pytanie brzmi: „Jak bezpiecznie usunąć…” Oczywiście, w mojej odpowiedzi założono, że kontrola wersji. Zakłada się, podobnie jak kopanie w koszu w celu znalezienia wartościowego. Żadne ciało, które ma jakikolwiek sens, nie wyrzuca kodu, a kontrola wersji powinna być częścią rygorów każdego dewelopera.
Problem dotyczy kodu, który może być zbędny. Nigdy nie możemy wiedzieć, że fragment kodu jest zbyteczny, chyba że możemy zagwarantować, że 100% ścieżek wykonania go nie osiągnie. Zakłada się, że jest to wystarczająco duże oprogramowanie, że gwarancja jest prawie niemożliwa. I chyba, że źle przeczytam pytanie, cały ten wątek rozmowy jest istotny tylko dla przypadków podczas produkcji, w których może zostać wywołany usunięty kod, a zatem mamy problem z czasem wykonania / produkcją. Kontrola wersji nie oszczędza nikogo z powodu błędów produkcyjnych, więc komentarz na temat „Kontrola wersji” nie ma związku z pytaniem lub moim pierwotnym punktem, tj. Odpowiednio przestarzałym w bardzo długim okresie czasu, jeśli naprawdę musisz, inaczej nie „
IMHO, komentarz jest zbyteczny i może zostać usunięty.
źródło
Może kompilator to zauważył, po prostu nie robi to zamieszania.
Uruchom kompilację z pełną optymalizacją kompilatora, dostosowaną do rozmiaru. Następnie usuń podejrzany kod i ponownie uruchom kompilację.
Porównaj pliki binarne. Jeśli są identyczne, kompilator zauważył i po cichu usunął kod. Możesz go bezpiecznie usunąć ze źródła.
Jeśli pliki binarne są różne ... to nie jest jednoznaczne. To może być jakaś inna zmiana. Niektóre kompilatory zawierają nawet datę i godzinę kompilacji w pliku binarnym (i być może można je skonfigurować!)
źródło
Tak naprawdę ostatnio spotkałem się z tą dokładną sytuacją, stosując metodę o nazwie „deleteFoo”. Nigdzie w całej bazie kodu nie znaleziono tego łańcucha poza deklaracją metody, ale mądrze napisałem wiersz dziennika na górze metody.
PHP:
Okazuje się, że kod został użyty! Niektóre metody AJAX wywołują „method: delete, elem = Foo”, która jest następnie łączona i wywoływana za pomocą call_user_func_array () .
W razie wątpliwości zaloguj się! Jeśli upłynął wystarczający czas bez wypełnienia dziennika, rozważ usunięcie kodu. Ale nawet jeśli usuniesz kod, zostaw dziennik na miejscu i komentarz z datą, aby ułatwić znalezienie zatwierdzenia w Git dla faceta, który musi go zachować!
źródło
Skorzystaj z
grep
jakichkolwiek dostępnych wcześniej narzędzi, możesz znaleźć odniesienia do kodu. (Nie pierwotnie moja instrukcja / rada.)Następnie zdecyduj, czy chcesz zaryzykować usunięcie kodu, czy też moja sugestia może się przydać:
Możesz zmodyfikować funkcję / blok kodu, aby zapisać, że został użyty w pliku dziennika. Jeśli taka wiadomość nie zostanie „kiedykolwiek” zapisana w pliku dziennika, prawdopodobnie możesz usunąć kod logowania.
Dwa problemy:
Pobieranie dziennika od innych użytkowników. Być może możesz ustawić globalną flagę, która spowoduje awarię programu przy wyjściu i uprzejmie poprosić użytkownika o przesłanie dziennika błędów.
Śledzenie dzwoniącego. W niektórych językach wysokiego poziomu (np. Python) można łatwo uzyskać informacje zwrotne. Ale w skompilowanych językach musisz zapisać adres zwrotny w dzienniku i wymusić zrzut pamięci przy wyjściu (punkt 1).
W C powinno to być dość proste: użyj bloku warunkowego (np. #Ifdef ... #endif), aby użyć tego tylko wtedy, gdy wiesz, że zadziała (i przetestowałeś, że działa), i po prostu przeczytaj adres zwrotny ze stosu (może wymagać lub nie wbudowanego języka asemblera).
Zapisanie argumentów w pliku dziennika może, ale nie musi być przydatne.
źródło
Jeśli wiesz, co robi otaczający go kod, przetestuj go, aby sprawdzić, czy nie działa. Jest to najprostszy i najbardziej opłacalny sposób na refaktoryzację fragmentu kodu, nad którym pracujesz, i należy to zrobić w każdym razie w celu opracowania wszelkich zmian w kodzie, nad którym pracujesz.
Jeśli natomiast refaktoryzujesz fragment kodu, w którym nie wiesz, co on robi, nie usuwaj go . Zrozum to najpierw, a następnie refaktoryzuj, jeśli określisz, że jest bezpieczny (i tylko jeśli możesz ustalić, że refaktoryzacja byłaby właściwym wykorzystaniem twojego czasu).
Teraz mogą zaistnieć pewne okoliczności (wiem, że sam na to wpadłem), w których kod „martwy” nie jest w rzeczywistości powiązany z niczym . Takich jak biblioteki kodu opracowane przez kontrahenta, które nigdy nie zostały nigdzie zaimplementowane, ponieważ stały się przestarzałe natychmiast po dostarczeniu aplikacji (dlaczego tak, mam na myśli konkretny przykład, skąd wiesz?).
Osobiście skończyłem z usuwaniem całego tego kodu, ale jest to ogromne ryzyko, że nie polecam lekceważyć - w zależności od tego, ile kodu ta zmiana może potencjalnie wpłynąć (ponieważ zawsze istnieje możliwość, że coś przeoczyłeś) powinien być wyjątkowo ostrożny i przeprowadzać agresywne testy jednostkowe, aby ustalić, czy usunięcie tego starożytnego starszego kodu spowoduje uszkodzenie aplikacji.
Biorąc to wszystko pod uwagę, prawdopodobnie nie warto tracić czasu ani rozsądku, aby próbować usunąć taki kod. Nie, chyba że stanie się poważnym problemem z twoją aplikacją (na przykład niemożność ukończenia skanowania bezpieczeństwa z powodu rozdęcia aplikacji ... dlaczego tak, wciąż mam na myśli przykład), więc nie poleciłbym go, dopóki nie dotrzesz taka sytuacja, w której kod naprawdę zaczął gnić w imponujący sposób.
Krótko mówiąc - jeśli wiesz, co robi kod, najpierw go przetestuj. Jeśli nie, prawdopodobnie możesz zostawić to w spokoju. Jeśli wiesz, że nie możesz tego zostawić w spokoju, poświęć swój czas na agresywne przetestowanie zmiany przed jej wdrożeniem.
źródło
Moje podejście, które zawsze zakładałem, że jest w tym przypadku standardem branżowym, jak dotąd nie zostało wspomniane:
Zdobądź drugą parę oczu.
Istnieją różne rodzaje „nieużywanego kodu”, a w niektórych przypadkach usunięcie jest właściwe, w innych przypadkach należy je przefiltrować, a w innych przypadkach ucieka się tak daleko, jak to możliwe i nigdy nie oglądać się za siebie. Musisz dowiedzieć się, który to jest, i aby to zrobić, najlepiej połączyć się z drugą - doświadczoną! - programista, który bardzo dobrze zna podstawy kodu. To znacznie zmniejsza ryzyko niepotrzebnej pomyłki i pozwala wprowadzić niezbędne ulepszenia, aby utrzymać bazę kodu w stanie umożliwiającym utrzymanie. A jeśli tego nie zrobisz, w pewnym momencie zabraknie Ci programistów, którzy dobrze znają bazę kodu.
Widziałem też podejście do rejestrowania - ściślej mówiąc, widziałem kod rejestrujący: jeszcze więcej martwego kodu, który pozostawiono tam przez 10 lat. Podejście do logowania jest całkiem przyzwoite, jeśli mówisz o usunięciu całych funkcji, ale nie, jeśli usuwasz tylko niewielki fragment martwego kodu.
źródło
Prosta odpowiedź na twoje pytanie brzmi: nie możesz bezpiecznie usunąć kodu, którego nie rozumiesz, co obejmuje również niezrozumienie, jak się nazywa. Będzie pewne ryzyko.
Będziesz musiał ocenić, jak najlepiej ograniczyć to nieuniknione ryzyko. Inni wspominali o logowaniu. Rejestrowanie może oczywiście udowodnić, że jest używane, ale nie może udowodnić, że nie jest używane. Ponieważ głównym problemem związanym z martwym kodem jest to, że jest on utrzymywany, być może uda ci się dodać komentarz mówiący, że jest to podejrzany martwy kod, i nie zajmować się nim.
Jeśli kod znajduje się pod kontrolą źródła, zawsze możesz dowiedzieć się, kiedy został dodany i ustalić, jak został wtedy wywołany.
W końcu albo to zrozumiesz, zostaw w spokoju, albo zaryzykuj.
źródło
Jeśli twórca danego kodu jest nadal dostępny, zapoznaj się z jego usunięciem . Również przeczytać komentarze w kodzie .
Otrzymasz właściwe wyjaśnienie, dlaczego ten kod został dodany i dla jakiej funkcji służy. Może z łatwością stanowić wsparcie dla konkretnego przypadku użycia, a nawet może nie mieć wystarczającej wiedzy specjalistycznej, aby ocenić, czy tak naprawdę nie jest to wymagane. W skrajnym przypadku można usunąć wszystkie bezpłatne instrukcje z programu C i nie zauważyć niczego złego (zabraknie pamięci może potrwać kilka minut).
To naprawdę zła kultura, kiedy autor kodu siedzi obok ciebie, ale nie widzisz powodu do rozmowy, ponieważ jesteś pewien, że on po prostu pisze zły kod, a twój jest tak doskonały. I, oczywiście, po prostu pisze losowe słowa w komentarzach, nie trzeba tracić czasu na czytanie.
Jednym z najważniejszych powodów napisania komentarza w kodzie jest wyjaśnienie DLACZEGO został on zaimplementowany w ten sposób. Możesz nie zgodzić się z rozwiązaniem, ale musisz wziąć pod uwagę problem.
Dyskusja z autorem może również ujawnić, że kod jest historyczną pozostałością czegoś usuniętego lub nigdy nieukończonego, więc można go bezpiecznie usunąć.
źródło
Zdecydowanie zgadzam się z pierwszą uwagą Markusa Lausberga: Kod należy zdecydowanie przeanalizować za pomocą narzędzi. Mózgom małp nie można ufać przy tego rodzaju zadaniach !!
Większość standardowych języków programowania można stosować w IDE, a każde IDE, które warto nazywać, ma funkcję „znajdź dla wszystkich zastosowań”, która jest dokładnie właściwym narzędziem do tego rodzaju sytuacji. (Na przykład Ctrl + Shift + G w Eclipse)
Oczywiście: narzędzia analizatora statycznego nie są idealne. Należy zdawać sobie sprawę z bardziej skomplikowanych sytuacji, w których decyzja o wywołaniu danej metody ma miejsce tylko w czasie wykonywania i nie wcześniej (wywołania przez odbicie, wywołania funkcji „ewaluacyjnych” w językach skryptowych itp.).
źródło
Dwie możliwości:
źródło