Legacy system to nie wyrok. To etykieta, którą zbyt często przykleja się do każdego kodu starszego niż trzy lata, choć realny problem bywa dużo bardziej precyzyjny: konkretny moduł, konkretna integracja, konkretny fragment logiki biznesowej, który nikt już nie rozumie. Decyzja “przepisujemy wszystko” albo “zostawiamy jak jest” to najgorsza możliwa decyzja, bo ignoruje to, że system to zbiór komponentów o różnym wieku, różnej jakości i różnym poziomie ryzyka.

Szybka odpowiedź

Modernizuj istniejący kod, gdy problem dotyczy wybranych modułów, a architektura jako całość jest sensowna. Przepisuj od zera, gdy technologia jest martwa, dług techniczny przekracza 70% kodu, a koszt utrzymania przewyższa koszt budowy nowego systemu. Decyzję podejmuj na podstawie audytu, nie przeczucia.

Najważniejsze wnioski

  • Refaktoryzacja modułowa jest tańsza niż pełny rewrite w większości audytowanych przypadków, bo pozwala zachować działającą logikę biznesową i unikać podwójnych kosztów utrzymania.
  • Decyzję o modernizacji legacy systemu warto oprzeć na trzech osiach: wieku kodu, liczbie integracji i ryzyku biznesowym, a nie na subiektywnym odczuciu “ten kod jest stary”.
  • Przepisanie systemu od zera ma sens tylko przy martwej technologii, chaotycznych integracjach i braku dokumentacji wiedzy biznesowej.
  • Pełny rewrite generuje koszt równoległego utrzymania dwóch systemów przez 12-24 miesiące, co rzadko jest ujmowane w wstępnych szacunkach budżetowych.
  • Audyt techniczny przed decyzją powinien trwać maksymalnie 2-4 tygodnie i kończyć się konkretną mapą modułów z priorytetami, nie ogólnym raportem “system wymaga modernizacji”.

Macierz decyzyjna: trzy osie, które naprawdę mają znaczenie

Większość firm podejmuje decyzję o modernizacji na podstawie jednego kryterium: wieku systemu. To błąd, bo dziesięcioletni system napisany w PHP z solidną architekturą może być w lepszym stanie niż dwuletni projekt sklecony na szybko pod deadline inwestora. W EXOLAB przy audytach legacy systemów pracujemy na trzech osiach, które razem dają realny obraz sytuacji.

Wiek kodu ma znaczenie, ale nie sam w sobie, tylko w połączeniu z tym, czy technologia jest wciąż wspierana. System w Ruby on Rails 4 z 2015 roku to inny problem niż system w .NET Framework, który Microsoft aktywnie wygasza. Pierwsza sytuacja to kwestia aktualizacji zależności, druga to realne ryzyko braku wsparcia i podatności bezpieczeństwa, których nikt już nie łata.

Liczba i typ integracji decyduje o tym, jak bardzo system jest “wpleciony” w resztę infrastruktury firmy. System z trzema integracjami (płatności, CRM, mailing) to inna skala ryzyka niż system z 40 integracjami do systemów partnerskich, gdzie każda zmiana w rdzeniu może wywołać efekt domina. Im więcej integracji, tym bardziej rewrite od zera zwiększa ryzyko regresji, bo trzeba odtworzyć nie tylko logikę, ale wszystkie kontrakty API, często niedokumentowane.

Ryzyko biznesowe to oś najczęściej ignorowana przez zespoły techniczne, a najważniejsza dla zarządu. Pytanie nie brzmi “jak stary jest kod”, tylko “co się stanie, jeśli ten moduł przestanie działać na tydzień”. Moduł fakturowania, który generuje przychód, ma inny priorytet niż panel raportowy używany przez dwie osoby w dziale marketingu.

Nakładając te trzy osie na siebie, dostaje się realną mapę: moduły stare, mocno zintegrowane i krytyczne biznesowo wymagają najostrożniejszej, stopniowej modernizacji legacy systemu. Moduły stare, izolowane i niekrytyczne można przepisać szybko i bez większego ryzyka, a czasem po prostu wyłączyć.

Kiedy refaktoryzacja wystarczy

Refaktoryzacja starego oprogramowania firmy ma sens, gdy problem jest lokalny, a nie systemowy. Typowe sygnały: kod działa poprawnie, ale jest trudny w utrzymaniu, brakuje testów, dokumentacja jest szczątkowa, ale logika biznesowa jest w miarę zrozumiała po analizie. W takich przypadkach inwestycja w testy regresyjne, wydzielenie modułów i stopniowe wprowadzanie wzorców projektowych daje szybszy zwrot niż rewrite.

Dobrym sygnałem do refaktoryzacji jest też sytuacja, w której zespół rozumie co system robi, ale nie rozumie jak to jest zaimplementowane. To oznacza, że wiedza biznesowa jest zachowana, a problem jest czysto techniczny. Rewrite w takiej sytuacji jest ryzykowny, bo łatwo zgubić subtelne reguły biznesowe wpisane w warunki brzegowe kodu, które nigdy nie zostały spisane w żadnej specyfikacji.

Refaktoryzacja działa też dobrze przy podejściu przyrostowym znanym jako strangler pattern: nowe funkcjonalności budujesz w nowej, lepszej architekturze, a stary kod stopniowo “okrążasz” i wygaszasz moduł po module. System nigdy nie przestaje działać, a ryzyko biznesowe rozkłada się na miesiące, nie kumuluje się w jednym wielkim wdrożeniu.

Kiedy rewrite jest uzasadniony

Są sytuacje, gdzie refaktoryzacja to tylko odkładanie problemu. Jeśli technologia jest martwa (brak wsparcia, brak developerów na rynku, brak aktualizacji bezpieczeństwa od lat), żadna ilość refaktoryzacji tego nie naprawi. Podobnie, gdy architektura jest fundamentalnie błędna, na przykład monolit, w którym logika biznesowa, warstwa danych i UI są ze sobą tak splecione, że zmiana jednej linijki wymaga testowania całego systemu.

Przepisanie systemu od zera koszt jest zwykle znacznie wyższy niż firmy zakładają na starcie, bo szacunki rzadko uwzględniają okres równoległego utrzymania starego systemu. W praktyce rewrite trwający 12-18 miesięcy oznacza podwójne koszty infrastruktury, podwójne koszty supportu i ryzyko, że nowy system w dniu wdrożenia będzie miał mniej funkcji niż stary, co wywołuje frustrację użytkowników i presję na przyspieszenie, a to prowadzi do kolejnego długu technicznego.

Rewrite ma sens, gdy dług technologiczny obejmuje większość kodu, integracje są chaotyczne i niedokumentowane, a wiedza biznesowa nie jest zaszyta w kodzie tylko w głowach ludzi, których można przepytać. Ma też sens, gdy skala biznesu drastycznie wzrosła i architektura, która obsługiwała 100 zamówień dziennie, fizycznie nie skaluje się do 10 tysięcy, niezależnie od tego, ile się ją poprawi.

KryteriumRefaktoryzacjaRewrite od zera
Wiek technologiiWspierana, aktualizowalnaMartwa, brak wsparcia
Liczba integracjiWysoka, dobrze zdefiniowanaChaotyczna, niedokumentowana
Wiedza biznesowaZaszyta w kodzie i zespoleRozproszona, trudna do odtworzenia
Czas realizacji3-6 miesięcy na moduł12-24 miesiące
Ryzyko przestojuNiskie, wdrożenie przyrostoweWysokie, wdrożenie skokowe
Koszt utrzymania w trakcieJeden systemDwa systemy równolegle

Case study: audyt, który obalił założenie o rewrite

Klient z branży e-commerce B2B zgłosił się z gotową decyzją: system do zarządzania zamówieniami, napisany dziewięć lat wcześniej, miał zostać przepisany od zera. Zarząd zakładał budżet i 14-miesięczny harmonogram na podstawie wcześniejszej wyceny od innego zespołu. Zanim zaakceptowaliśmy zakres, zaproponowaliśmy dwutygodniowy audyt techniczny, który klient początkowo traktował jako formalność przed podpisaniem umowy na rewrite.

Audyt podzielił system na 14 modułów i ocenił każdy według macierzy trzech osi: wiek, integracje, ryzyko biznesowe. Wynik był zaskakujący. Dziewięć modułów, w tym rdzeń logiki zamówień i fakturowania, było napisanych solidnie, miało rozsądną separację warstw i brakowało im głównie testów automatycznych oraz aktualizacji zależności. Problem koncentrował się w pięciu modułach: starym systemie raportowania, integracji z jednym z magazynów zewnętrznych, module powiadomień e-mail i dwóch fragmentach kodu odpowiedzialnych za rabaty, gdzie logika biznesowa była chaotyczna i słabo przetestowana.

Zamiast pełnego rewrite zaproponowaliśmy modernizację modułową: przepisanie od zera tylko tych pięciu problematycznych modułów, refaktoryzację i pokrycie testami rdzenia systemu, oraz stopniowe wprowadzenie nowej warstwy integracji API zgodnej ze standardami, które ułatwiły dodanie kolejnych partnerów magazynowych w przyszłości. Cały proces zajął 5 miesięcy i kosztował około 40% pierwotnie zakładanego budżetu na pełny rewrite. System działał przez cały czas modernizacji, bez przestojów krytycznych dla biznesu, a zespół klienta uczył się nowej architektury stopniowo, moduł po module, zamiast być rzucony na głęboką wodę w dniu wielkiego wdrożenia.

Ten przypadek nie jest wyjątkiem, to raczej reguła w projektach, gdzie decyzję podejmuje się po audycie, nie przed nim. Firmy, które od razu zakładają rewrite, zwykle robią to z frustracji wobec konkretnego, widocznego problemu, np. wolnego raportowania czy błędnej integracji, i uogólniają go na cały system, choć realny problem dotyczy 20-30% kodu.

Jak przeprowadzić audyt przed podjęciem decyzji

Dobry audyt legacy systemu nie powinien trwać miesięcy. Dwa do czterech tygodni wystarczają, żeby ocenić kluczowe moduły, o ile audyt ma jasno zdefiniowany zakres i nie próbuje przeanalizować każdej linijki kodu z jednakową dokładnością. Pierwszym krokiem jest mapowanie modułów i ich zależności, najlepiej wizualne, żeby zarząd (nie tylko dział IT) zobaczył, które części systemu są ze sobą splecione, a które można ruszyć niezależnie.

Drugim krokiem jest ocena pokrycia testami i jakości dokumentacji, bo to bezpośrednio przekłada się na ryzyko każdej zmiany, niezależnie od tego, czy wybierze się refaktoryzację czy rewrite. Trzecim, często pomijanym elementem, jest rozmowa z użytkownikami biznesowymi systemu, nie tylko z developerami. Osoby obsługujące system na co dzień często wiedzą, które funkcje są krytyczne, a które to relikt procesu, który firma przestała stosować pięć lat temu, ale nikt nie usunął odpowiadającego mu kodu.

Wynikiem audytu powinna być konkretna mapa priorytetów z rekomendacją dla każdego modułu: zostaw, refaktoryzuj, przepisz, albo wygaś. Bez tego poziomu granularności decyzja o modernizacji legacy systemu zawsze będzie strzałem na oślep, niezależnie od tego, jak dobrze brzmi w prezentacji dla zarządu.

Słowniczek

Legacy system

Istniejące oprogramowanie, zwykle starsze, które nadal działa w firmie i obsługuje kluczowe procesy, ale bywa trudne w utrzymaniu z powodu przestarzałej technologii, braku dokumentacji lub odejścia autorów kodu.

Dług techniczny

Suma kompromisów i uproszczeń wprowadzonych w kodzie w celu szybszego dostarczenia funkcjonalności, które w przyszłości generują dodatkowy koszt utrzymania i modernizacji.

Strangler pattern

Strategia modernizacji, w której nowa funkcjonalność jest budowana równolegle do starego systemu i stopniowo go zastępuje, moduł po module, bez konieczności jednorazowego, ryzykownego wdrożenia całości.

Refaktoryzacja

Zmiana wewnętrznej struktury kodu bez zmiany jego zewnętrznego zachowania, mająca na celu poprawę czytelności, testowalności i łatwości dalszego rozwoju.

Audyt techniczny

Systematyczna ocena stanu systemu obejmująca architekturę, jakość kodu, pokrycie testami, integracje i ryzyko biznesowe, stanowiąca podstawę do decyzji o dalszym rozwoju systemu.

Najczęściej zadawane pytania

Jak długo trwa modernizacja legacy systemu w porównaniu do przepisania go od zera?

Modernizacja modułowa zwykle trwa 3-6 miesięcy na kluczowe obszary i można ją prowadzić bez przerywania działania systemu. Pełny rewrite to zazwyczaj 12-24 miesiące, a firma przez cały ten czas musi utrzymywać stary system równolegle, co podwaja koszty operacyjne.

Czy refaktoryzacja starego oprogramowania firmy jest tańsza niż przepisanie systemu od zera?

W większości przypadków tak, zwłaszcza gdy problem dotyczy konkretnych modułów, a nie całej architektury. Przepisanie od zera bywa uzasadnione tylko wtedy, gdy technologia jest martwa, integracje są chaotyczne, a dług techniczny obejmuje ponad 70% kodu.

Jakie są największe ryzyka przepisania systemu od zera?

Największe ryzyka to utrata wiedzy biznesowej zaszytej w starym kodzie, wydłużenie projektu ponad plan, koszty utrzymania dwóch systemów równolegle i tzw. efekt second-system, gdzie nowy system dziedziczy te same błędy projektowe co stary.

Od czego zacząć decyzję o modernizacji legacy systemu?

Zacznij od audytu technicznego obejmującego wiek kodu, liczbę i typ integracji, pokrycie testami oraz mapę ryzyka biznesowego. Bez tych danych decyzja między refaktoryzacją a rewrite jest zgadywanką, a nie wyborem popartym liczbami.

Czy można modernizować system stopniowo, bez pełnego rewrite?

Tak, to najczęściej stosowane podejście zwane strangler pattern. Nowe funkcjonalności buduje się w nowej architekturze, a stary system stopniowo się okrąża i wygasza moduł po module, minimalizując ryzyko przestoju.