Software Development

Java

Legacy code – bajka dla (nie)grzecznych programistów?

Grafika rysunkowa symbolizująca potwora, jakim może być Legacy Code

Dawno, dawno temu pewien Król dowiedział się, że jego poddani mogliby pracować lepiej i przynosić królestwu więcej pożytku, gdyby mieli System, który usprawniłby ich pracę. Zwołano więc radę Mędrców i stworzono Dokument, w którym każdy z nich napisał, czego pragnie. Ten z kolei został przekazany Wielkiemu Managerowi, który wezwał Programistów i powiedział – Tu macie plan. A teraz do roboty! Ma być gotowe na wczoraj!

Programiści zamknęli się z Dokumentem w swoim, całkiem zresztą wygodnym, lochu. Zaczęli myśleć, stukać palcami w klawiatury i znowuż myśleć. Na bieżąco uzupełniano im kadzie z gorącą kawą oraz zapasy pizzy i hamburgerów. Po kilku tygodniach Wielki Manager mógł w końcu pokazać Królowi ich dzieło - System. Wszystkim od razu się spodobał, był ładny i dla każdego miał być bardzo przydatny. Dano mu nawet własną komnatę w zamku i regularnie karmiono danymi. Właściwie ta bajka powinna zakończyć się zdaniem: „I żyli długo i szczęśliwie”. Niestety, jak to bywa w życiu, finał nie był tak oczywisty. Okazało się, że System trzeba uczyć rzeczy, o których wcześniej nawet największym Mędrcom się nie śniło. Programiści oraz sami Mędrcy zmieniali więc System coraz częściej. Doszywali mu kolejne nogi, macki, dodatkowe głowy czy ogony. Powoli stawał się coraz brzydszy, większy i zajmował kolejne komnaty i piętra zamku. Aż w końcu stał się tak ogromny, że prawie się w nim nie mieścił. Jego twórcy uciekli z królestwa, chociaż krążyły też plotki, że to System ich pożarł. A System rósł dalej i powoli zamieniał się w strasznego Potwora, którego nikt już nie lubił i o którego nikt nie dbał.

Wcześniej czy później każdy programista w zetknie się w swojej karierze z projektem, w którym przyjdzie mu zmierzyć się z takim Potworem.

Legacy code (kod odziedziczony lub zastany), jest to kod działający już pewien czas na produkcji. Tworzony przez ludzi, którzy nie są już obecni w naszej organizacji lub firmy zewnętrzne, które już nie istnieją na rynku. Bardzo często taki kod nie posiada dokumentacji, a początkowe założenia w trakcie jego działania ulegały wielu zmianom. Z różnych powodów nie znajdziemy w nim również testów jednostkowych ani integracyjnych lub napisane wcześniej testy na skutek zbyt wielu zmian i wprowadzania dodatkowych zależności przestały być miarodajne.

Właściwie to wszyscy programiści kochają nienawidzić legacy code. Nie jest on piękny, nie ma w nim nowoczesnych i „modnych” technologii, nie pozwala nam na „twórczą” pracę. Trzeba jednak pamiętać, że ostatecznie i tak każdy kod stanie się legacy. To trochę tak, jak z ludzkim organizmem. Rozwija się, starzeje i jeśli nie dbamy o niego, i nie utrzymujemy w odpowiedniej formie, w końcu zaczyna chorować, i z czasem odmawia posłuszeństwa. A więc, jeśli nawet początkowo taki kod był naprawdę dobrze zaprojektowany i napisany, obecnie może wyglądać jak słynny potwór Frankensteina pozszywany z kilkudziesięciu kawałków. Praca z takim kodem, pomimo wielu trudności, jakie napotkamy, wcale nie musi być dla programisty przekleństwem. Taki projekt daje nam szansę na naukę podstaw programowania, stosowania właściwych technik, wzorców projektowych, pisania czystego kodu i dobrych testów. 

Co robić? Jak żyć?

0. “Erem terem tytyrytki, zgiń, potworze, boś ty brzydki!” (S.Lem Cyberiada)

Niestety w przypadku potwora legacy code to zaklęcie rzadko działa. Pokusa jest jednak wielka: czy nie lepiej napisać wszystko od początku?  Czasami tak. Zawsze jednak musi to być decyzja przemyślana i poparta szczegółową analizą. Bez wiedzy o systemie, jego działaniu i zależnościach pomiędzy funkcjonalnościami lepiej nie podejmować się tego zadania.  A przy działającym produkcyjnie systemie może to nie być po prostu opłacalne. 

1. Zaprzyjaźnij się z Potworem.

Żeby skutecznie i bezpiecznie utrzymywać czy zmieniać system legacy code musisz poznać jak właściwie działa. Biorąc pod uwagę wspomniany wcześniej brak dokumentacji i dodawane, czasami przez lata, nowe funkcjonalności, jest to proces dość trudny i czasochłonny. Czasami nie pozostaje nic innego jak „przeklikiwanie” każdej metody czy klasy i debugowanie kodu linijka po linijce. Dość skuteczny jest w takim przypadku, tak zwany scratch refactoring, który polega, w dużym uproszczeniu, na skopiowaniu kodu produkcyjnego do osobnego projektu, swoistej piaskownicy. Tam następnie przeprowadza się refactoring, pisze testy, sprawdza, jak nasz Potwór zachowa się, kiedy odetniemy jedną czy drugą mackę. Taki refactoring pozwala na szybką i skuteczną naukę zależności, jakie istnieją pomiędzy elementami systemu, bez ryzyka popsucia czegokolwiek w kodzie produkcyjnym. Najważniejsze jednak w tej metodzie jest to, że całość tych zmian MUSIMY usunąć. Oczywiście zdobytą wiedzę można i należy wykorzystać! Z doświadczenia wiem, że bez wiedzy o działaniu kodu, bardzo łatwo poprawiając jedną rzecz, zepsuć kilka innych.

2. Fantastyczne Zwierzęta i jak o nie dbać.

Kiedy w trakcie poprawiania starych lub wprowadzania nowych funkcjonalności poznajesz zastany kod, zapisuj to, czego się nauczyłeś. Twórz i uaktualniaj dokumentację kodu - nawet najdrobniejszy fragment wiedzy może okazać się w przyszłości bezcenny. Staraj się również nie zatrzymywać tej wiedzy tylko dla siebie. Pamiętaj, że tak zwany „BUS FACTOR” w projekcie zawsze powinien być większy od 1. W trochę makabrycznym tłumaczeniu wyznacza on liczbę członków zespołu, którzy muszą zostać „przejechani przez autobus”, żeby projekt nie mógł być kontynuowany. Jeśli więc w naszym projekcie „bus factor” = 1, oznacza to, że wypadek losowy, choroba lub po prostu decyzja o kontynuowaniu kariery poza projektem tego jednego, jedynego pracownika spowoduje, że nie będzie nikogo, kto zapanuje nad naszym Potwornym kodem.

3. Tresuj Potwora, kiedy tylko się da.

Testy to niejako oswajanie naszej Bestii. Pisanie testów przy okazji wprowadzania najmniejszej nawet zmiany, używanie TDD (Test Driven Development), doprowadzi w końcu do momentu, w którym większość kodu zostanie pokryta testami, a jego utrzymanie oraz implementacja kolejnych zmian będą znacznie łatwiejsze i bezpieczniejsze. Co więcej, pisząc testy będziesz mógł lepiej zrozumieć działanie kodu. Takie podejście powinno stać się nawykiem i pierwszą rzeczą, jaką robisz. Paradoksalnie zdarzają się przypadki, kiedy stopień skomplikowania kodu jest tak duży, a nasza wiedza o jego działaniu tak niewielka, że bardzo ciężko napisać sensowne testy. Może tu właśnie jest miejsce na napisanie danej funkcjonalności zupełnie od nowa?

4. Refactoring - pielęgnacja potwornego kodu.

Ulepszanie samej struktury kodu, po to, aby był bardziej zrozumiały i tańszy w utrzymaniu, bez jednoczesnej zmiany jego działania, czyli właśnie refactoring, to w przypadku potwornego kodu odpowiednik szczotki do sierści i obcinaczki do pazurów. Często musimy pogodzić się z tym, że nie wszystkie pomysły, nawet te najlepsze, uda się od razu wcielić w życie. Ale możemy, a nawet powinniśmy wprowadzać czasem nawet niewielkie zmiany w zastanym kodzie tak, aby za każdym razem zostawić go w nieco lepszym stanie. Pozostawienie kodu takiego, jakim jest, na zasadzie: “Jak działa, to nie ruszać!”, nie jest w tym wypadku dobrym pomysłem. Zmieniający się model biznesowy, standardy czy przepisy prawa w danym kraju wymuszają na nas czasem wprowadzanie zmian w dość szerokim zakresie. Takim przykładem są zmiany w przepisach dotyczących ochrony danych osobowych (RODO), które spowodowały konieczność modyfikacji wielu systemów przetwarzających tego typu dane. Implementując nowe funkcjonalności staraj się, aby kod był „czysty” i utrzymuj go w takim właśnie stanie. Nie dodawajmy więcej „potworności” do tych, które już mamy! Przede wszystkim jednak, jak radzi słynny Przewodnik Autostopem po Galaktyce – don’t panic! Ostatecznie kod, z którym masz do czynienia, czasami nawet przez długi czas działał bezbłędnie na produkcji. Poza tym, powiedz szczerze, czy nie zdarzyło Ci się patrząc po jakimś czasie na własny kod napisany przed kilkoma miesiącami zastanawiać się, ze zgrozą “Kto to właściwie napisał?!” Zamiast więc narzekać, jak bardzo potworny jest kod, z którym się masz zmierzyć, postaraj się żeby była to naprawdę dobra zabawa, która przy okazji będzie nauką na przyszłość. Ktoś kiedyś powiedział, że jeśli będziesz robił to co lubisz, nie przepracujesz ani jednego dnia w swoim życiu. Czego pozostaje życzyć nam wszystkim.

Zdjęcie profilowe Artura Kolibskiego

Artur Kolibski

Od kilku lat związany z Britenet, aktualnie pracuje jako Java Developer w projekcie z legacy code. W branży IT już od kilkunastu lat. Wcześniej pracował w korporacji i zdobywał doświadczenie w bankowości i finansach.