Sytuacja kobiet w IT w 2024 roku
10.07.20197 min
Michael Litwin

Michael LitwinIT Content Writer

Jak radzić sobie z Legacy Code

Skuteczne metody na zwalczanie strachu przed starym kodem oraz zabezpieczenie go na przyszłość, by już nigdy nikomu nie groził. 

Jak radzić sobie z Legacy Code

Legacy Code, czyli ten stary kod lub oprogramowanie, którego nikt nie chce dotknąć, nikt nie wie, kto go napisał, a wszyscy boją się go zastąpić. Większość deweloperów doświadczyła tego niepokojącego uczucia konieczności dłubania w starym komponencie, aby go rozszerzyć lub naprawić błąd. W najlepszym przypadku jest to niejasny fragment kodu, który jest odpowiedzialny za niewielką funkcję, w najgorszym to rdzeń całego systemu. 

Stary kod to postrach wielu programistów. Na pewno wielu z nas znalazło się w sytuacji, gdy dostaliśmy ciekawe zadanie lub zlecenie, przygotowaliśmy nasze narzędzia i wiedzę, nastawiliśmy się na wyzwanie. Jednak w dniu, w którym zobaczyliśmy, z czym mamy do czynienia, cały zapał wyleciał nam w pierwszym oddechu rozczarowania. Przed nami stoi stara baza kodu. A my musimy do niej wprowadzić nowoczesne funkcje i kompatybilność. 

W wielu przypadkach praca ze starym kodem to jak czytanie starosłowiańskich tekstów. Nigdy nie jesteśmy w stanie być w 100% pewni, czy zły lub niekompletny kod, na który patrzymy, to efekt słabej pracy programisty, słabych warunków programowania, czy po prostu realia czasów kodu łupanego. Nie zmienia to faktu, że zanim kod ten stanie się kompatybilny z naszą pracą, minie dużo czasu na jego naprawę. A może nawet przepisanie?

Nie przejmuj się. Mamy kilka porad dla Ciebie, jak poradzić sobie z pracą z Legacy Code. Ale najpierw spróbujmy go zdefiniować. 

Definicja

Istnieje kilka definicji Legacy Code, niektórzy tłumaczą to pojęcie dość dosłownie z angielskiego na “Kod dziedziczony”. Ta ponura definicja ma swoje podstawy, w pewnym sensie robi to wrażenie odziedziczenia przestarzałego majątku. Niby mamy całość na tacy, ale nie potrafimy wprost zrozumieć, co dziadek napisał w swoich notatkach, gdzie w tym starym polonezie jest wlewnik, gdzie są korki w odziedziczonym mieszkaniu, jak działa piec z lat 70-tych. 

Na forach spotkałem się z pojęciem, że Legacy Code to każdy kod, który już przy pierwszym spojrzeniu wzbudza lęk. Gdzie indziej widziałem tezę, że każdy kod staje się Legacy w momencie publikacji.

Autorem najpopularniejszej jednak definicji jest Michael Feathers w książce zatytułowanej „Working Effectively with Legacy Code”. Definiuje on Legacy Code jako „kod bez testów”. Według tej definicji, kod nie musi być stary. Wystarczy, by nikt go nie przygotował na przyszłość. 

Moim zdaniem, wszystkie współgrające razem definicje opisują to, z czym mamy do czynienia. Legacy Code to najczęściej stary kod, którego nie możemy łatwo rozszyfrować, który budzi nasz lęk i który nie został przystosowany do tego, by był serwisowany przez kogoś innego niż autora w przyszłości. 

Przejdźmy zatem do tego, jak sobie z nim prawidłowo radzić. 

Nie oceniaj autora kodu

Po pierwsze, wstrzymaj się od niesprawiedliwej oceny autora kodu. Nie znamy warunków, w jakich kod powstał i dlatego nie możemy być na sto procent pewni, że jego autor był przygotowany na to, że firma będzie go używać tak długo lub że odda go w ręce kogoś innego, lata później. 

Czy aby na pewno powinieneś ruszać Legacy Code?

Tomasz Choduń, Software Architect z firmy WebInterpret, którą niedawno nagrodziliśmy tytułem Top Tech Employer, udzielił nam bardzo wartościowego komentarza eksperckiego na temat Legacy Code. 

Legacy Code to zazwyczaj kod, który działa. Niekoniecznie wiadomo, jak dokładnie działa, ale z jakiegoś powodu jest potrzebny, przynosi naszej organizacji korzyści. Gdyby tak nie było, kod zostałby usunięty, albo już dawno poddany refaktoryzacji.

Legacy Code związany jest też często z pojęciem długu technologicznego, wprowadzonym przez Warda Cunninghama. Przy pracy z kodem odziedziczonym istotne jest, aby nie powiększać długu technologicznego, zwłaszcza długu niestrategicznego (zaciągniętego przypadkiem, z niewiedzy, bez istotnych przesłanek, bez planowania spłaty).

W Webinterpret z racji 10-letniej historii mamy oczywiście do czynienia z Legacy Code. Praca nad zmianami w takim kodzie nie jest łatwa, jest jednak konieczna, aby istniejące rozwiązania wspierały nasz ciągły wzrost, a przy odpowiedniej dyscyplinie i narzędziach całkiem efektywna. Praca ta musi się odbywać przy akceptacji „biznesu” i obustronnym zrozumieniu, że widoczne pozytywne efekty nie pojawią się od razu. Żeby trochę odczarować negatywne skojarzenia z Legacy Code”i nie wystraszyć inżynierów, używamy często określenia „vintage software”.

Jeżeli razem z kodem uzyskałeś jego testy, wykonaj je

Jeżeli razem z kodem dostałeś jego testy (co według definicji Michaela Feathersa już nie czyni go starym kodem), przeprowadź je. Poczynając od testów dla klasy metody. Powinno to uchronić działanie systemu przed zepsuciem pod wpływem zmian. Jeżeli nic się nie zepsuło, powinieneś powoli poznawać działanie aplikacji. Następnie przeprowadź mock zależności i na bieżąco weryfikuj wywołania. Jeżeli uzyskasz wyjątek, mockuj jego zachowanie. Ostatecznie powinieneś zrozumieć co dzieje się w danym fragmencie kodu, jakich wymaga parametrów i jakie wywołuje zależności. 

Teraz gdy rozumiesz już, jak działa Twój kod, jesteś w stanie przeprowadzić delikatną refaktoryzację. 

Co to jest refaktoryzacja

Refactoring lub refaktoryzacja kodu, to po prostu metodyka restrukturyzacji istniejącego kodu, bez zmiany jego podstawowej funkcji. Często można rozwiązać większość problemów lub błędów, po prostu dodając małe zmiany. Pamiętaj, że praca na cudzym kodzie jest trudna, a refaktoryzacja nie zawsze jest prostym rozwiązaniem, nawet jeśli wymaga jedynie zrozumienia fragmentu kodu, krok po kroku. Może zająć dużo czasu, ale jest bezpiecznym rozwiązaniem, które, o ile kod jest wystarczająco dobrze otestowany, zawsze pozwoli ci się cofnąć o krok w przypadku, w którym coś nie działa.

Refaktoryzacja czy przepisanie?

W rzadkich przypadkach, przepisanie kodu od nowa, może się okazać skuteczniejszym i mniej bolesnym rozwiązaniem, a możliwie i przydatniejszym długoterminowo. Jednak przepisanie danego fragmentu systemu rzadko jest dobrym pomysłem, ponieważ wymaga bardzo dobrego zrozumienia tego, co robi ten fragment. Bez odpowiedniego poziomu znajomości najczęstszym scenariuszem jest wkopanie się w niemożliwe do wykonania zadanie.

Części Legacy w systemach często odgrywają ważną rolę w funkcjach, o których nie mamy pojęcia, oraz obsługują bardzo wiele nieudokumentowanych przypadków. Dlatego, jeżeli po naprawieniu jednego buga twierdzisz, że byś to wszystko przepisał, to ostudź swój entuzjazm i spędź więcej czasu na sprawdzeniu, co tak naprawdę jest problemem w danym fragmencie Code base'u.

Jeżeli nie posiadasz testów i zmuszony jesteś działać na własną rękę, aby zrozumieć czy lepiej przepisać kod, czy dokonać refaktoryzacji, przeprowadź testy charakteryzacyjne

Testy charakteryzacyjne

W zależności od tego, jak stary i skomplikowany jest kod, możliwe, że ludzie polegają na nim na jeszcze dziwniejsze sposoby, niż mógłbyś sobie wyobrazić. Zanim cokolwiek z tym zrobisz, powinieneś nauczyć się o kodzie jak najwięcej, poznać i skatalogować możliwie dużo jego zachowań.

Testy charakteryzacyjne, to zautomatyzowane testy, które sprawdzają działanie starego kodu i chronią go przed zmianami, które mogą zniszczyć funkcjonalność. Ponownie, za tą metodę podziękujemy Michaelowi Feathersowi. Twoim celem nie jest ustalenie, co system powinien zrobić, ale co faktycznie robi. Odpal, zaobserwuj, skataloguj, powtórz. 

Testy charakteryzacyjne to skuteczna metoda, dopóki mamy dla niej odpowiednie warunki, jak to, że kod można podzielić na stosunkowo izolowane skrawki funkcjonalności i że zakres wprowadzanych zmian jest stosunkowo wąski.

Jednak nie zawsze będzie to możliwe. Kod Legacy rzadko jest modularny i raczej nie ma spójnego interfejsu, za pomocą którego komunikuje się z innymi częściami systemu. Bardzo często posiada masę dziwacznych zależności (antywzorce big ball of mud czy stovepipe system) i może korzystać z zewnętrznych zasobów ze swojego wnętrza. Testy na granicach często nie będą wystarczające - głównie dlatego, że nie są one dobrze zdefiniowane. Istnieją jednak narzędzia, które można wykorzystać do zautomatyzowania końcowego testowania systemu, aby pomóc scharakteryzować kod, nie posiadając informacji o jego szczegółach.

Po dokładnym scharakteryzowaniu kodu możemy stopniowo wprowadzać zmiany. Testy powinny nas na to przygotować na tyle, byśmy mogli wprowadzać niewielkie zmiany, bez uszkodzenia funkcjonalności

Pamiętaj jednak, że zmiana jest nadal ryzykowna, nawet przy najlepszym wysiłku testów charakteryzacyjnych, stary system może być nieprzewidywalny. Dlatego zmiany, które wprowadzasz, musisz wprowadzać w najmniejszych możliwych krokach. Jest to jak najbardziej żmudna robota, jednak stosunkowo mniej żmudna niż cofanie się w poszukiwaniu błędu, który popełniłeś wcześniej. Małe kroki pomogą ci się upewnić, że testy przeszły pomyślnie.

Zakończenie

Pamiętaj, że pracując nad Legacy Code, musisz zadbać o to, by po Twoich zmianach nie wrócił zbyt szybko do tego samego poziomu kompatybilności, w jakim go zastałeś. Jednak gdy raz wykonasz dobrą robotę ze sporym kawałkiem Legacy Code, następna taka przygoda będzie dużo bardziej przystępna. Twoja praca powinna zacząć zapewniać lepsze pokrycie testowe, wzmocnić pewność siebie i wyeliminować strach przed starym kodem. 

Przy każdej pracy ze starym kodem, naszym celem powinno być doprowadzenie go do modułowości. Dlaczego to ma znaczenie? Cóż, ponieważ jeśli uczynisz kod źródłowy wystarczająco modularnym, możesz zacząć migrować jego części na nowsze platformy lub skorzystać z dobrodziejstw nowszych wersji języka czy frameworka.

Najgorsze, co możesz zrobić ze starym kodem, to spędzić nad nim wieki tylko po to, by za rok lub dwa ktoś musiał zrobić dokładnie to samo. Tak więc pamiętaj, bądź dokładny i pracowity, a zyskasz na pracy ze starym kodem najwięcej doświadczenia, oraz szacunku i wdzięczności tych, którzy pracować z nim będą w przyszłości. 

<p>Loading...</p>