Każdy z nas prędzej czy później będzie musiał poświęcić swój czas na pracę z kodem zastanym. Angielskojęzyczna część branży ma nawet taki ładny termin „Legacy Code”, który nie obejmuje jednak wszystkiego o czym chcę pisać.

W tym miejscu zajmiemy się podziałem kodu zastanego na kilka sposobów. Każdy z nich będzie opisywał kod w innym kontekście i będzie kładł nacisk na inne jego właściwości. Pojęcia z różnych grup mogą się wzajemnie wykluczać albo uzupełniać.

Podział ze względu na czas powstania

Kod można podzielić na kilka grup biorąc pod uwagę czas jego powstania. Czas ten rzutuje na to jak kod będzie wyglądał w środku, jakie wzorce i metody tworzenia kodu zostały użyte oraz jakie technologie możemy odnaleźć w kodzie.

Kod nowy

Jest to kod który otrzymaliśmy do pracy i wiemy, że powstał w miarę niedawno. Zazwyczaj tego typu kod charakteryzuje się użyciem bieżącej wersji języka, bibliotek w ostatnich stabilnych wersjach oraz jest zaprojektowany zgodnie z obecnie panującą modą. Oczywiście nie oznacza to, że kod jest dobry, ale pozwala przypuszczać, że przynajmniej wiadomo o co w nim chodzi. Ważną cechą jest też duża i aktualna „wiedza plemienna”. Zazwyczaj osoba, która tworzyła jakiś fragment kodu ma go w pamięci i wie o co chodziło. Na twórcach można wymusić np. dokumentację lub szkolenia. Kod jest w okresie świadczenia wsparcia przez twórców.
Problem stanowi stosunkowo duża ilość drobnych błędów, które wymknęły się w okresie testowania. Kod taki ma też tendencję do częstych i drobnych zmian.

Kod dojrzały

Jest to kod, który już się „uleżał”. Zazwyczaj wykonany jest w nowoczesnej technologii, a większość zależności jest w miarę aktualna. Co ciekawe w kodzie tym jest stosunkowo niewiele błędów, a te które istnieją są albo dobrze znane, albo mają status „won’t fix”. Jeżeli jakaś funkcjonalność nie ma dokumentacji to zazwyczaj nawet twórca nie będzie nam w stanie pomóc „od ręki” ponieważ „pamięć dobra, ale krótka”.
Taki kod stanowi większość kodu z jakimi przyjdzie nam pracować.

Kod przestarzały

Wykonany w starej wersji języka z wykorzystaniem niewspieranych wersji bibliotek lub co gorsza z różnymi swoimi wewnętrznymi hakami ponieważ w czasie jego tworzenia nie było gotowych rozwiązań.
Problemem są wyżej wspomniane „haki”. Można do nich też zaliczyć konieczność utrzymania wstecznej zgodności co powoduje problemy ponieważ nie można wykorzystać niektórych elementów języka np. typów generycznych. Zazwyczaj nie ma też osób, które wiedzą co kod robi. Zatem braki w dokumentacji trzeba łatać na własną rękę.
Co ciekawe w kodzie tym objawiają się „nowe, nieznane funkcjonalności”. Wynika to ze statystycznej szansy na prowadzenie danych w czasie trwania odpowiedniej fazy trzeciego księżyca Saturna względnie z wprowadzenia nowej funkcjonalności „bo to mała robota przecież”.

Kod starożytny

Napisany w języku, którego nie używa się już od lat. Zazwyczaj programistów tego języka szuka się w tych częściach biurowca, o których zwyczajowo mówi się loch. Tu chciałbym pozdrowić kolegów Krzyśka i Włodka. Tak panowie jesteście dinozaurami, ale w przeciwieństwie do dinozaurów wasze szkielety mało kogo obchodzą 😉
Problemem jest zazwyczaj to, że nie rozumiesz kodu.

Podział ze względu na dokumentację i testy

Kod można też podzielić pod względem dostarczonej z nim dokumentacji oraz zestawu testów. Tu warto pamiętać, że „moda na testowanie” kodu jest stosunkowo nowa. Nie ma zatem co liczyć na zestawy testów jednostkowych w kodzie przestarzałym, a tym bardziej w starożytnym. Zakładam, że 85% pokrycia testami oznacza dobrze przetestowany kod.

Kompletna dokumentacja i testy pokrywające ponad 85% kodu

Kod marzenie. Pracę z nim warto zacząć lektury i po kilku(nastu) dniach nie powinniśmy mieć żadnych problemów z opanowaniem tego co się dzieje.

Kompletna dokumentacja i brak testów

Dokumentacja pozwala na pracę z kodem, ale by analizować konkretne przypadki trzeba samodzielnie napisać testy. Zazwyczaj jest to syndrom kodu, który jest bardziej korpo niż agile. Należy uważać na haki, bo niektóre elementy dokumentacji w pierwszym rzucie są implementowane w ten sposób. Później ze względu na brak testów nikt tego już nie rusza by nie spieprzyć.

Brak dokumentacji i testy pokrywające ponad 85% kodu

Sytuacja odwrotna do poprzedniej. Kod jest bardziej agaile niż korpo. Oznacza to, że by go zrozumieć musimy przeczytać zarówno kod jak i testy. Specyficzną cechą tego typu kodu jest jego niestabilność i częste zmiany. Wiele zespołów odkłada tworzenie dokumentacji do czasu ustabilizowania się kodu. Problemem może być niejasne określenie co kod powinien robić. Brak jasnych założeń co do poszczególnych elementów i funkcji może powodować poważne problemy.

Niekompletna dokumentacja i testy

Najczęściej spotykany rodzaj kodu. Komuś chciało się to napisał testy. Ktoś inny miał czas to skrobnął dokumentację. Zaletą tego kodu jest to, że zazwyczaj istnieje dokumentacja dotycząca założeń poszczególnych elementów, a braki dotyczą już konkretnej dokumentacji. Wadą to, że psują się rzeczy nieudokumentowane.

Brak dokumentacji i testów

Najgorszy rodzaj kodu. Jeżeli tylko możesz nie przejmuj go, ani nie dotykaj. Kod tego typu jest najczęściej spotykany tam gdzie coś było robione „z doskoku, przez takiego jednego najemnego, za 100PLN”. Zazwyczaj jest to pomieszanie z poplątaniem zarówno jeżeli chodzi o styl kodowania jak i użyte komponenty. Jeżeli zostaniecie zmuszeni do modyfikacji takiego kodu to pamiętajcie, że jest to jeden z niewielu przypadków kiedy przepisanie od nowa jest tańsze niż poprawa istniejącego kodu.

Podział ze względu na łatwość testowania (i modyfikacji)

Ostatni sposób podziału jaki proponuję to podział pod kątem łatwości testowania kodu. Testowania zarówno jednostkowego jak i integracyjnego. Łatwość testowania przekłada się w prost na łatwość modyfikacji kodu. Jeżeli kod jest łatwy w przetestowaniu to łatwo go też modyfikować bez ryzyka otrzymania „nowej, przełomowej funkcjonalności i to zupełnie za darmo”. W porównaniu do poprzednich ten podział jest stosunkowo prosty.

Kod łatwy w testowaniu

Łatwo jest napisać test. Nie ma potrzeby konfigurowaniu wielu zależności, a dla każdego zestawu danych wejściowych jest jednoznaczny zestaw danych wyjściowych. Jeżeli kod jest łatwy w testowaniu to najprawdopodobniej jest bardzo dobrej jakości.

Kod trudny w testowaniu

Testowanie takiego kodu wymaga użycia skomplikowanych narzędzi, dużo czasu na konfigurację. Zaletą jest to, że po skonfigurowaniu dla danych danych wejściowych co do zasady otrzymujemy spójne dane wyjściowe. Wszelkie odchylenia są zazwyczaj spowodowane różnicami w konfiguracji środowiska testowego i produkcyjnego.

Kod nietestowalny

Jest to kod, który nie może zostać przetestowany w żaden rozsądny sposób. Problemem może być na przykład brak narzędzi lub zbyt silne związanie kodu z konkretną architekturą sprzętową. Do tej grupy przez bardzo długo zaliczano kodu UI. Należy też pamiętać, że kiedyś nie pisano kodu pod kątem jego testowania. Zatem najczęściej jest to kod przestarzały i przeprowadzenie testów wymaga odpowiedniego interfejsu białkowo-kofeinowego.

Podsumowanie

Gdzie w tym wszystkim jest „Legacy Code” (LC) i dlaczego kod zastany to szersze pojęcie? LC to podzbiór zawierający kod nacechowany najgorszymi przypadłościami. Pozbawiony dokumentacji, wsparcia, trudny w testowaniu i zazwyczaj napisany w „nieżyciowy” sposób. Kod zastany to cały kod, w którego produkcji nie uczestniczyliśmy. Zatem nawet moduł dostarczony przez inny zespół w większym projekcie jest dla nas kodem zastanym. To czy jest LC to już inna sprawa.