Skip to main content
idego
Rozwój Oprogramowania

Utrzymanie systemów legacy: Fortran, COBOL i inne klasyczne technologie

Autor: Idego Group

Utrzymanie systemów legacy: Fortran, COBOL i inne klasyczne technologie

Wiele systemów, które po cichu obsługują nasz świat, powstało na długo przed tym, jak dzisiejsi programiści pojawili się w branży. Fortran, którego korzenie sięgają 1957 roku, pozostaje fundamentem obliczeń naukowych i inżynieryjnych — modele klimatyczne, mechanika płynów, symulacje jądrowe i przetwarzanie sejsmiczne wciąż na nim polegają. COBOL, niemal równie sędziwy, obsługuje znaczącą część światowych transakcji bankowych, rejestrów państwowych, list płac i polis ubezpieczeniowych. Poza nimi PL/I wciąż działa w środowiskach mainframe, Pascal i Delphi napędzają systemy POS i automatykę przemysłową, klasyczny Visual Basic 6 tkwi w niezliczonych narzędziach wewnętrznych, a ręcznie pisany assembler wciąż steruje urządzeniami wbudowanymi pracującymi od dziesięcioleci.

To nie są akademickie ciekawostki. Branżowe szacunki sytuują globalną bazę kodu COBOL daleko powyżej 200 miliardów linii — większość z nich uruchamia się bez nadzoru w produkcji każdej nocy. Voyager 1 i 2, obecnie najdalej oddalone od Ziemi obiekty stworzone przez człowieka, wciąż są sterowane oprogramowaniem napisanym głównie w Fortranie i asemblerze w latach 70. Amerykański IRS, Social Security Administration oraz większość kluczowych systemów rezerwacji lotniczych nadal opierają się na kodzie z epoki mainframe. Te systemy stanowią infrastrukturę nowoczesnego życia.

Dlaczego systemy legacy przeżywają

Cyniczna odpowiedź na pytanie "dlaczego wciąż tu są?" brzmi: "bo nikt nie odważy się ich zastąpić". Realistyczna odpowiedź jest bardziej złożona.

Symulacja w Fortranie dopracowywana przez trzydzieści lat w lotnictwie czy modelowaniu klimatu zawiera ogromną ilość niejawnej wiedzy — każdy numeryczny przypadek brzegowy, każdy workaround dla osobliwości pierwotnego solvera, każdą poprawkę zwalidowaną względem fizycznych pomiarów. Zadanie wsadowe w COBOL-u zamykające księgi banku każdej nocy zostało przez dziesięciolecia skorygowane jeden przypadek brzegowy na raz — każda poprawka odzwierciedla rzeczywisty incydent. Wyrzucenie tego kodu oznacza wyrzucenie tej wiedzy instytucjonalnej. Odbudowa od zera rzadko jest tańsza, szybsza ani bezpieczniejsza niż utrzymanie tego, co działa.

Słynne porażki przepisywania potwierdzają tę lekcję. Kilka dużych banków, państwowych administracji podatkowych i linii lotniczych wspólnie zainwestowało miliardy w programy modernizacyjne, które ostatecznie zostały okrojone lub porzucone. Wszystkie ostrzegawcze historie mają mniej więcej ten sam scenariusz: niedoszacować osadzonej logiki biznesowej, natknąć się na nieoczekiwane ograniczenia integracyjne, wyczerpać budżet lub wolę polityczną i wrócić do utrzymywania systemu legacy mimo wszystko.

Realia utrzymania

Zespoły wspierające stacki legacy regularnie wpadają na ten sam zestaw przeszkód.

Specjalistów jest mało i jest ich coraz mniej. Większość uczelni przestała uczyć Fortrana 77 czy COBOL-a dziesięciolecia temu. Pozostali eksperci są często kilka lat od emerytury, a strumień następców jest cienki. Gdy starszy inżynier odchodzi, zabiera ze sobą kontekst, którego żaden dokument nie utrwala.

Toolchain rozkłada się. Oryginalne kompilatory, środowiska kompilacji i debuggery często działają wyłącznie pod systemami operacyjnymi, które same nie są już wspierane. Typowy pipeline kompilacji dla legacy zależy od konkretnej wersji kompilatora od dostawcy, który już nie istnieje, działającej na wydaniu serwera, które straciło łatki bezpieczeństwa lata temu.

Testów zwykle brakuje. Większość systemów legacy powstała przed erą, w której automatyczne testowanie było standardem. Zapewnienie jakości realizowano ręcznie, często przez ekspertów dziedzinowych, którzy wiedzieli, jakie liczby powinny pojawić się w którym raporcie. Gdy ci ludzie odchodzą na emeryturę, system staje się czarną skrzynką, której nikt nie potrafi z czystym sumieniem zmodyfikować.

Wiedza plemienna dominuje. Krytyczne założenia żyją w głowach kilku weteranów, a nie w żadnym dokumencie. "Nigdy nie uruchamiaj wsadu zamknięcia miesiąca w pierwszy dzień roboczy po roku przestępnym" to przykład reguły, która istnieje wyłącznie w czyjejś pamięci.

Integracja jest niezgrabna. Nowoczesne systemy muszą rozmawiać z tymi stackami przez interfejsy, jakie są dostępne — pliki płaskie, rekordy o stałej szerokości, zrzuty FTP o trzeciej w nocy, screen-scraping przez emulatory terminali 3270 albo własnościowe protokoły binarne. Każdy taki interfejs to źródło kruchości.

Formaty danych nie są tym, czym myślisz. EBCDIC zamiast ASCII, packed decimal zamiast natywnych liczb całkowitych, copybooki COBOL nakładające wiele układów rekordów na te same bajty, bloki COMMON we Fortranie po cichu współdzielące pamięć między podprogramami, binarne zrzuty mixed-endian ze starych stacji uniksowych — "prosty" eksport danych rzadko jest prosty, a jeden źle zinterpretowany bajt może zepsuć miliony rekordów, zanim ktokolwiek to zauważy.

Praktyczny playbook

Nie ma jednej dobrej odpowiedzi, ale kilka podejść sprawdza się w praktyce.

1. Najpierw uczyń system obserwowalnym, dopiero potem cokolwiek zmieniaj. Dodaj logowanie, monitoring i powtarzalne buildy. Uchwyć obecne zachowanie w testach charakteryzujących — testach zapisujących rzeczywiste wyjścia dla znanych wejść, niezależnie od tego, czy zachowanie jest formalnie poprawne. To odwrócenie klasycznego TDD: w systemach legacy traktujesz istniejącą implementację jako oracle, dopiero potem refaktoryzujesz pod tą siatką bezpieczeństwa. Bez tego kroku każda zmiana jest zgadywanką.

2. Ustabilizuj toolchain. Wsadź build do kontenera lub maszyny wirtualnej, nad którą masz kontrolę. Przypnij wersje kompilatorów. Przenieś kod do nowoczesnego systemu kontroli wersji, jeśli jeszcze tam nie jest. Upewnij się, że świeży checkout potrafi wyprodukować działający binarz na nowo zaaprowizowanej maszynie — bez tego system jest jedną awarią dysku od bycia nieodzyskiwalnym. Open-source'owe zamienniki jak GnuCOBOL, GFortran czy Free Pascal czasem mogą zastąpić wycofane komercyjne kompilatory, ale dopiero po starannym sprawdzeniu równoważności wyników.

3. Opakuj, nie zastępuj. Wystaw rdzeń legacy przez cienkie nowoczesne API — REST, gRPC lub kolejkę komunikatów — żeby wokół można było budować nową funkcjonalność w nowoczesnych językach. Taki wrapper pełni rolę warstwy antykorupcyjnej (anti-corruption layer), tłumacząc między legacy'owym a współczesnym modelem danych. To fundament wzorca strangler fig: migrujesz kawałek po kawałku, wycofując fragmenty starego systemu dopiero po zwalidowaniu zamiennika w produkcji.

4. Dokumentuj agresywnie, zwłaszcza dlaczego. Kod źródłowy odpowiada na pytanie, co system robi. Decyzje, kompromisy i historyczne incydenty mówią następnemu inżynierowi, dlaczego robi to w taki, a nie inny sposób — i to właśnie ten kontekst znika, gdy starsi pracownicy odchodzą. Architecture Decision Records, runbooki dla każdego cyklicznego zadania operacyjnego i krótkie narracje przewodnie po skomplikowanych modułach zwracają się wielokrotnie.

5. Łącz odchodzących ekspertów z następnym pokoleniem. Traktuj kadry jak ryzyko strategiczne, nie sprawę HR. Łącz seniorów z juniorami przy realnej pracy utrzymaniowej, nie ćwiczeniach szkoleniowych. Niech senior głośno opisuje swoje rozumowanie. Nagrywaj te sesje, jeśli możesz. Cel: zamienić najdroższą formę wiedzy organizacyjnej — to, co jest w czyjejś głowie — na najtańszą, czyli to, co jest spisane.

6. Modernizuj najpierw obrzeża. Najbardziej ryzykowna jest podstawowa logika biznesowa. Najmniej ryzykowne są zwykle obrzeża wejścia/wyjścia — importy plików, generowanie raportów, interfejsy użytkownika, integracje. Zacznij stamtąd. Zastąp interfejs typu green-screen nowoczesnym webowym frontendem. Zamień importy plików płaskich na pipeline'y zdarzeniowe. Każde udane podmienienie obrzeża buduje pewność organizacji i zdejmuje jedno z ograniczeń, nie naruszając niezastąpionego rdzenia.

7. Zaplanuj eleganckie wyjście, nie heroiczny rewrite. Jeśli system ma zostać wycofany, rób to w horyzoncie wieloletnim z jasno wyznaczonymi off-rampami. Zidentyfikuj procesy biznesowe, które można przenieść na nowoczesną platformę. Zidentyfikuj te, które naprawdę wymagają systemu legacy. Redukuj zakres aż do momentu, gdy to, co zostało, jest wystarczająco małe, by przepisać je bezpiecznie — albo by utrzymywać je bezterminowo jako odizolowany komponent.

Pułapki danych i integracji warte podkreślenia

Większość projektów modernizacyjnych niedoszacowuje warstwy danych. Kilka konkretnych pułapek wraca regularnie.

Niezgodności kodowania znaków to cisi zabójcy. EBCDIC i ASCII porządkują znaki inaczej, więc sortowanie, które poprawnie działało na mainframe, da subtelnie błędne wyniki, gdy dane skopiuje się na maszynę linuksową — a błąd może ujawnić się dopiero, gdy klient zgłosi reklamację po miesiącach. Formaty liczbowe są równie zdradliwe: packed decimal w COBOL-u przechowuje dwie cyfry na bajt i nie jest bezpośrednio interpretowalny przez żaden nowoczesny język bez jawnej konwersji. Kod Fortranu używający bloków COMMON współdzieli regiony pamięci między podprogramami w sposób niewidoczny dla narzędzi analizy statycznej — refaktoryzacja takiego kodu bez zrozumienia współdzielonego układu to gwarantowany sposób na wprowadzenie niedeterministycznych błędów.

Integrując się z takimi systemami, traktuj każdy interfejs jak niezaufany, nawet jeśli był "stabilny" przez dwadzieścia lat. Dodaj walidację schematu na granicy. Loguj każdy rekord, który nie przechodzi. Nigdy nie zakładaj, że pole pod offsetem 47 to to, co mówi dokumentacja — zweryfikuj na danych produkcyjnych.

Aspekt ekonomiczny

Utrzymywanie systemu legacy rzadko bywa efektowne, ale często jest najbardziej racjonalnym wyborem ekonomicznym. Udany rewrite jest drogi i ryzykowny; nieudany rewrite bywa katastrofalny — czasem zagrażający istnieniu organizacji. Zdyscyplinowane utrzymanie połączone ze stopniową modernizacją obrzeży zwykle dostarcza więcej wartości za każdą wydaną złotówkę niż big-bang rewrite i utrzymuje światło w produkcji, podczas gdy zapadają długoterminowe decyzje.

Właściwe pytanie rzadko brzmi: "przepisać czy utrzymywać?". Brzmi: "które części tego systemu rzeczywiście muszą zmienić się w ciągu najbliższych trzech lat i jaka jest najtańsza ścieżka do tego rezultatu, która nie zagrozi produkcji?". W takiej ramie modernizacja przestaje być decyzją binarną i staje się ciągłym portfelem małych, odwracalnych zakładów.

Podsumowanie

Systemy napisane w Fortranie, COBOL-u i ich rówieśnikach to nie relikty. To działająca infrastruktura, która w wielu przypadkach robi swoją robotę lepiej niż cokolwiek, co próbowano w jej miejsce wprowadzić. Traktowanie ich z taką samą starannością i dyscypliną, jaką dałbyś dowolnemu innemu systemowi produkcyjnemu — observability, testy, kontrola wersji, dokumentacja, planowanie sukcesji — to właśnie to, co utrzyma je w ruchu przez kolejną dekadę.

Cel to nie zachowanie przeszłości dla niej samej. To danie biznesowi swobody wybrania kolejnego ruchu na własnych warunkach, zamiast bycia zmuszonym do panicznego rewrite'u, gdy ostatnia osoba rozumiejąca system w końcu wyjdzie za drzwi.

Powiązane artykuły