7 min read

Legacy buster: Kiedy historia puszcza do ciebie oko

Legacy buster: Kiedy historia puszcza do ciebie oko
Photo by Markus Spiske / Unsplash

Jest to mój pierwszy wpis i chciałbym Ci serdecznie podziękować za zainteresowanie. Ze względu na nazwę bloga – Legacybuster – zdecydowałem się zacząć od tematu, którym jest właśnie legacy.
W świecie technologii termin legacy code wywołuje szerokie spektrum emocji - od niechęci, przez obawę, po nostalgię. Dla wielu programistów to synonim problemów, z którymi muszą się mierzyć na co dzień, jednakże czy na pewno każda linijka starego kodu zasługuje na to miano? W tym artykule przyjrzymy się, czym naprawdę jest kod legacy, jak powstaje oraz jakie wyzwania i dylematy wiążą się z jego utrzymaniem i modyfikacją. Rozważymy różnorodne scenariusze i zastanowimy się, kiedy warto podjąć wyzwanie refaktoryzacji, a kiedy lepiej pozostawić istniejące rozwiązania w spokoju. Czy jesteśmy gotowi stawić czoła temu dziedzictwu?

No dobrze, to lecimy.

Legacy to po prostu stary kod, czy aby tylko?

Myślę że termin ten w naszej branży nie powinien być nikomu obcy, ale z kronikarskiej przyzwoitości zacznę od krótkiego wyjaśnienia.

Generalnie termin legacy w wolnym tłumaczeniu oznacza "Dziedzictwo" czyli coś pozostawionego "w spadku", tylko kto komu co pozostawia? Zastanówmy się nad tym przez chwilę.

Utarło się, że legacy to kod, z którym jest pewnego rodzaju problem, np. nikt w zespole go nie zna lub do jego stworzenia wykorzystane zostały przestarzałe technologie, a czasami jest wynikiem braku testów. I bardzo często tak jest. Jednak czy zawsze? Niekoniecznie. Legacy możemy sobie pozostawić równie dobrze sami ;) np. kiedy pracujemy niestarannie, wytwarzając kod niedbale lub pomijając ogólnie przyjęte normy oraz dobre praktyki.

Mianownik jednak jest jeden, ostatecznie legacy to kod, który powoduje negatywne odczucia, a może nawet strach przed nieświadomym wprowadzeniem usterki podczas jego modyfikacji, nieważne jakie są tego przyczyny.

Jak do tego doszło? Nie wiem.

Dobrze, wiemy już czym jest kod legacy, ale jak to się stało, że nasz piękny kodzik to legacy?
Mam niestety złą nowinę, każdy kod kiedyś stanie się legacy, bez względu na to czy nam się to podoba czy nie. Od nas jedynie zależy, jak, kiedy i w jakich okolicznościach do tego dojdzie.
Dlaczego? Bo oprogramowanie, a właściwie cały sektor wytwarzania oprogramowania, rozwija się bardzo dynamicznie i coś co dzisiaj oceniamy, jako złoty grall programowania, jutro ktoś inny może ocenić jako legacy.

Klasyczne legacy

W pierwszej kolejności przedstawię wam chyba najbardziej naturalny sposób powstania legacy. Czy pamiętacie taki język programowania jak COBOL? Jeśli nie, to śpieszę z wyjaśnieniem. Był to bardzo popularny język programowania wykorzystywany na przykład w sektorze finansowym, ze względu na jego czytelną składnię, niezawodność oraz wsteczną kompatybilność. Tak, kiedyś był to nowoczesny język programowania z czytelną składnią.

IDENTIFICATION DIVISION.
PROGRAM-ID. IDSAMPLE.
ENVIRONMENT DIVISION.
PROCEDURE DIVISION.
    DISPLAY 'HELLO WORLD'.
    STOP RUN.

I teraz nasuwa się pytanie - kiedyś? Był? Co się z nim stało?
No właśnie, ten cały kod nigdzie nie wyparował, nadal można się na niego natknąć na przykład w systemach bankowych. Ktoś go kiedyś napisał. No a teraz? Teraz się już tak nie pisze. Teraz mamy nowoczesne języki jak Rust, czy Kotlin, które wyznaczają standardy nowoczesności. Ale nadal ten kod w języku COBOL jest i działa bardzo często na produkcji, a co lepsze, zarabia pieniądze. Bardzo często jest też nienajgorzej napisany. Problem w tym, że mamy już stosunkowo mało specjalistów go znających, więc wiele osób dałoby mu plakietkę "LEGACY" i prawdopodobnie słusznie.

Nowe legacy

O ile poprzedni przykład, poza wiekiem, może nie wykazywać oczywistych uchybień, o tyle na drugim biegunie legacy mamy kod, gdzie faktycznie coś poszło nie tak.
Zdecydowanie częściej legacy to po prostu kod zapisany niedbale z pominięciem wszelkich norm i dobrych praktyk.

Jako przykład podam mój własny z przeszłości, kiedy pracowałem nad firmowym komunikatorem. Mieliśmy bardzo sztywno ustawiony deadline oraz finansowanie, a do tego pracowaliśmy w zespole bardzo szybko i nie koniecznie zgodnie z ogólnie przyjętymi normami. Pisałem wtedy moduł kalendarza do zarządzania spotkaniami. Ostatecznie, udało się go wdrożyć i klient był nawet zadowolony, ale po miesiącu postanowił wprowadzić pewne modyfikacje w działaniu. Miesiąc może wydawać się relatywnie krótkim okresem czasu, jednak spoglądając na siebie wtedy, mogę powiedzieć, że mimo tak krótkiego czasu, "udało" mi się wtedy wytworzyć legacy. Dlaczego tak uważam? Ponieważ zmiany, które miałem wtedy wykonać, były trudne i nie oczywiste do przeprowadzenie, a ja czułem realny dyskomfort podczas pracy z tym kodem.

Legacy, ale na własnych warunkach

Czasami zespół nie jest świadom, że właśnie przyczynia się do powstania legacy, ale w bardzo specyficznych sytuacjach może być to także świadoma decyzja.
Co świadoma decyzja? Ale jak to? A no tak. Czasami legacy może powstawać świadomie.

Tutaj przypomina mi się kolejna historia z mojej kariery, gdzie tworzyliśmy oprogramowanie wspierające sprzedaż. W pewnym momencie, nie wchodząc w szczegóły, moja firma brała udział w przetargu i liczył się "Time-To-Market", to znaczy, że ścigaliśmy się z innymi firmami o to kto będzie realizował zlecenie, a jedynym parametrem był czas. W tamtym momencie decyzja o wygenerowaniu długu technicznego była świadomą decyzja biznesową. Mieliśmy wybór,

  • albo pracować zgodnie ze wszelkimi normami oraz dobrymi praktykami, co oczywiście spowodowałoby wydłużeniem całego procesu i na koniec zaprezentować jedynie proste demo dla klienta, co wiązałoby się tym, że klient wystawiający przetarg prawdopodobnie wybrałby kogoś innego
  • albo zdążyć na czas zaprezentować efektowne demo i pozyskać nowy kontrakt.
    Członkowie zarządu zdecydowali się na świadome przyspieszenie prac kosztem obniżenia jakości na potrzeby demo, dzięki czemu możliwe było zwycięstwo w przetargu i kontynuacja prac projektowych jeszcze przez następne kilka lat.

Inne przyczyny

Omówiliśmy wiele powodów, ale świat nie zawsze jest taki prosty. Poza jawnymi naruszeniami dobrych praktyk przez nas, programistów, można spotkać szereg innych powodów czemu oprogramowanie, nad którym pracujemy, rozwija się wolniej.
Z biegu mogę wymienić takie:

  • Nieprecyzyjne wymagania
  • Niedostateczna analiza
  • Ograniczenia czasowe
  • Ograniczenia finansowe

Miejmy jednak na uwadze, że w żadnym wypadku nie jest to wyczerpująca lista, która zamyka temat.

Czy moje Legacy to w ogóle problem?

Tutaj warto się zatrzymać i spojrzeć na sytuację z dystansu. W poprzednich punktach opisałem, czym jest legacy. To kod, który sprawia trudności. Sztandarowym wyzwaniem związanym z legacy jest fakt, że programiści napotykają przeszkody podczas jego rozbudowy i wdrażania nowych funkcji. W tle cały czas przewija się motyw trudności w rozwoju lub ryzyka związanego ze zmianami.

Ale teraz się zastanówmy - rozważymy dwa przypadki, gdzie w obu będzie to kod legacy, ale charakterystyka pracy z nim będzie zupełnie inna.

Nowiutki moduł zarządzania krypto

Załóżmy, że jesteśmy początkującymi programistami i dowiedzieliśmy się o oprogramowaniu opartym na sztucznej inteligencji, które potrafi generować kod - Bajka. Przez dwa tygodnie pracy byliśmy tak produktywni, że udało nam się stworzyć lub wygenerować (hmm co za różnica, kod to kod) całe oprogramowanie do obsługi portfela kryptowalutowego. A testy? Kto by się tym przejmował, przecież będzie szybciej.

I tak pracowaliśmy i pracowaliśmy, aż przyszedł czas na oddanie oprogramowania do testów u klienta i nagle okazało się, że trzeba będzie wprowadzić zmiany w "naszym" kodzie. Cóż, pierwszy prompt? Hmm, nie działa. Drugi? Działa, ale przestało działać coś innego. Po kilku iteracjach bierzemy sprawę w swoje ręce. My nie damy rady?
No i po dniu czy dwóch zmagań okazało się, że no cóż, nie damy.

I teraz pytanie: czy to, co stworzyliśmy, można uznać za legacy? Z pewnością, mamy tu do czynienia z typowym przypadkiem oprogramowania, które może z powodzeniem stać się właśnie legacy. Brak testów, niezrozumiałe konstrukcje i techniki, które zostały użyte bez głębszego zrozumienia, oraz fakt, że część kodu mogła być wygenerowana przez narzędzie, zamiast pisana w pełni ręcznie, sprawiają, że utrzymanie i dalsza modyfikacja staje się problematyczna. A co gorsza, taki kod jest jeszcze trudniejszy do poprawienia, ponieważ nie do końca rozumiemy, jak wszystko działa.

I tutaj widać, jak ważne są testy, dokumentacja i przemyślana struktura kodu. Choć z pozoru mogło się to wydawać szybkim rozwiązaniem, w dłuższej perspektywie brak tych elementów, może okazać się znacznie bardziej kosztowny i ryzykowny.

Stary moduł obsługujący protokół YMODEM

Przeanalizujmy teraz taki scenariusz - Ktoś w naszej firmie (może nawet i my) kiedyś w 2005 roku zaimplementował bardzo stary protokół do przesyłania plików, w dajmy na to JAVA 1.6 - tak, pewne dinozaury pamiętają jeszcze taką wersję i ja też. Ponadto oprogramowanie nie ma napisanych testów, co sprawia, że wprowadzanie zmian mogłoby być potencjalnie kosztowne i ryzykowne. Protokół jednak mimo wszystko zaimplementowano bardzo starannie, a sam standard nie zmienił się w ogóle od tamtej pory. To znaczy, że w ciągu najbliższego roku albo dwóch nie będzie konieczności zmiany implementacji tego modułu.

I pytanie, czy to legacy stanowi rzeczywiście problem? W moim odczuciu, nie koniecznie. Oprogramowanie to może nie jest szczytem nowoczesności, ale spełnia swoją rolę, a co najważniejsze nie powoduje dyskomfortu podczas prób modyfikacji, bo takich modyfikacji nie ma i nie będzie. Pokusiłbym się nawet o stwierdzenie, że próby refaktoryzacji mogłyby wprowadzić więcej szkód niż samo legacy, bo ze względu na brak testów, podczas prób naprawienia struktury kodu, wprowadzone mogłyby zostać też regresje i nasz działający od wielu lat kod zacząłby działać wadliwie.

Werdykt - problem czy nie problem?

W tym miejscu wspomniałem dwie skrajności, kod legacy który jest stary, stabilny i nie ma potrzeby go zmieniać oraz taki, który powstał niedawno i będzie zmieniany bardzo często. Są to przykłady wymyślone, ale w prawdziwym życiu możemy się spotkać z podobnymi sytuacjami. Pamiętajmy jednak, że każdy przypadek jest inny i należy go przeanalizować indywidualnie. Zawsze warto się zastanowić jaki problem rozwiązujemy - czy nasz refaktor ma ułatwić nam pracę, czy jedynie poprawić nam samopoczucie i nakarmić ego.

Zakończenie

Podsumowując, kod legacy to nieodłączna część pracy każdego programisty, z którą przychodzi się mierzyć częściej, niż byśmy chcieli. W tym wpisie starałem się przybliżyć, czym jest legacy, skąd się bierze i jakie może mieć formy i myślę że udało mi się to przybliżyć.

Ostatecznie, każdy przypadek legacy powinien być rozpatrywany indywidualnie, z uwzględnieniem kontekstu, w którym funkcjonuje. Chociaż problem legacy może wydawać się złożony, to istnieją strategie i narzędzia, które pomagają w radzeniu sobie z technicznym długiem.

To jednak tylko wierzchołek góry lodowej. Temat legacy jest rozległy i z pewnością nie wyczerpuje go jeden wpis. Dlatego zapraszam do śledzenia mojego bloga, gdzie będę kontynuować tę dyskusję, dzieląc się dalszymi spostrzeżeniami oraz rozwiązaniami dotyczącymi pracy z zastanym kodem. Dziękuję za przeczytanie i do zobaczenia w przyszłych artykułach!