Modernizację starszej aplikacji zaczyna się od spisania, dlaczego musi się zmienić, a potem od wspólnej oceny kodu i produkcji. Przed zmianą struktury trzeba ustabilizować system i objąć testami zachowanie, od którego zależy biznes. Z taką siatką bezpieczeństwa system wymienia się kawałek po kawałku, dane przenosi się w przemyślany sposób, a pełne przepisanie od zera zostawia na rzadkie przypadki, gdy niewiele jest wartego zachowania.
Najpierw powód biznesowy, dopiero potem kod
Modernizacja jest kosztowna, więc zaczyna się od powodu. Typowe to środowisko uruchomieniowe lub framework bez wsparcia, zmiany trwające tygodniami, bo każde wydanie coś psuje, system, który rozumie tylko jedna osoba, albo platforma, która nie udźwignie tego, czego biznes potrzebuje w następnej kolejności. Każdy powód wskazuje inny pierwszy krok.
Powód należy zapisać razem z miarą, którą da się później sprawdzić, na przykład jak często odbywają się wydania, ile jest incydentów albo ile trwa, zanim typowa zmiana dotrze do użytkowników. Bez tego modernizacja staje się otwartym projektem technicznym, którego trudno bronić przy kolejnym przeglądzie budżetu.
Ocena kodu i produkcji przed jakąkolwiek decyzją
Ocena pokazuje, co faktycznie się ma. Powinna być krótka i kończyć się pisemnym raportem oraz rekomendowanym pierwszym krokiem. Trzeba czytać kod, ale też produkcję: logi, incydenty, wolne zapytania i sposób przeprowadzania wydań. Najgorsze problemy często leżą wokół kodu, a nie w nim.
- Wersje środowiska uruchomieniowego, frameworka i bibliotek oraz to, które z nich nie dostają już poprawek bezpieczeństwa
- Które części zmieniają się najczęściej, a które najczęściej się psują, na podstawie historii wersji i rejestru incydentów
- Pokrycie testami ścieżek, od których zależy biznes
- Jak aplikacja jest budowana, konfigurowana i wdrażana, i kto potrafi to zrobić
- Model danych, jego rozmiar i to, jakie inne systemy czytają lub zapisują tę samą bazę danych
- Osoby, które znają system, i to, co wiedzą tylko one
Najpierw stabilizacja produkcji, aby praca miała solidną podstawę
Jeśli system pada co tydzień, modernizacja będzie przerywana co tydzień. Najpierw należy usunąć przyczyny incydentów: dodać monitoring i alerty na krytycznych ścieżkach, zautomatyzować budowanie i wdrażanie, aby wydania były powtarzalne, oraz wynieść sekrety i konfigurację poza kod.
Te kroki zwracają się od razu i sprawiają, że każdy kolejny jest bezpieczniejszy. Szybko pokazują też, czy zespół potrafi zmieniać system, nie psując go, a to warto wiedzieć przed zobowiązaniem się do większego planu.
Siatka testów wokół zachowania, na którym polega biznes
Starszy kod ma zwykle mało testów, a jego udokumentowane zachowanie rzadko odpowiada rzeczywistemu. Przed zmianą struktury warto napisać testy charakteryzujące: testy, które zapisują, co system robi dziś, łącznie z jego nietypowymi przypadkami brzegowymi, tak aby każda zmiana zachowania ujawniła się jako nieprzechodzący test.
Najlepiej zacząć od krawędzi, od testów, które wywołują aplikację przez jej API lub interfejs użytkownika i sprawdzają wyniki, bo przetrwają zmiany wewnętrzne. W przypadku obliczeń i raportów warto przepuścić prawdziwe dane wejściowe przez stary i nowy kod i porównać wyniki. Bardziej szczegółowe testy dodaje się do każdej części w trakcie jej refaktoryzacji. Nasz artykuł o aktualizacji krytycznej aplikacji w Javie 8 pokazuje tę samą siatkę bezpieczeństwa zastosowaną przy aktualizacji środowiska uruchomieniowego.
Wymiana systemu kawałek po kawałku zamiast przepisywania
Pełne przepisanie wygląda na papierze czysto, ale stary system nadal działa i się zmienia, podczas gdy nowy próbuje go dogonić, a każde nieudokumentowane zachowanie trzeba po drodze odkryć na nowo. Dlatego przepisywanie od zera tak często trwa dłużej niż planowano, a biznes czeka.
Typową alternatywą jest wzorzec strangler fig. Przed starszą aplikacją umieszcza się warstwę routingu, na przykład reverse proxy lub API gateway. W nowym kodzie buduje się po jednej funkcji naraz i kieruje do niej ruch dla tej funkcji, gdy tylko się sprawdzi. Stary system się kurczy, aż można go wyłączyć, a biznes zyskuje wartość na każdym etapie.
Przepisanie od zera nadal bywa właściwą decyzją: gdy baza kodu jest mała, a jej zachowanie dobrze zrozumiane, albo gdy platformy, na której działa, nie da się utrzymać wystarczająco długo, by wymieniać system stopniowo. Decyzję podejmuje się według spisanych kryteriów, a nie z frustracji.
Dane jako osobna migracja
Kod można wymieniać fragmentami; dane trudniej podzielić. Dopóki stary i nowy kod współdzielą jedną bazę danych, każdą zmianę schematu trzeba koordynować. Warto wcześnie ustalić, który system jest źródłem prawdy dla każdego rodzaju danych, i unikać sytuacji, w której dwa systemy zapisują ten sam rekord bez reguły rozstrzygającej, który zapis wygrywa.
Gdy przenosi się funkcję, jej dane przenosi się lub synchronizuje w przemyślany sposób: skryptami migracyjnymi przetestowanymi na kopii produkcji albo ciągłą synchronizacją, dopóki działają oba systemy. Trzeba zaplanować, jak uzgadniać oba źródła, na przykład codziennymi zliczeniami i sumami kontrolnymi, i zachować możliwość powrotu, dopóki liczby się nie zgadzają.
Kolejność prac według ryzyka i wartości, z widocznymi postępami od początku
Prace warto ułożyć tak, by każdy krok albo zmniejszał ryzyko, albo dawał coś, co biznes widzi. Częsta kolejność to stabilizacja i automatyzacja, dodanie testów, aktualizacja środowiska uruchomieniowego, a potem wydzielanie funkcji, które zmieniają się najczęściej. Stabilne i rzadko ruszane części mogą poczekać, czasem bez końca.
Plan powinien być krótki i przeglądany po każdym kroku, bo zdobyta wiedza zmieni kolejność. Nasi inżynierowie pracowali w ten sposób przy krytycznych systemach. Za pośrednictwem Sopra Steria prowadzili migrację z Javy 8 do 16 krytycznej aplikacji do dziennego handlu gazem, a jako część zespołu programu chmurowego Crédit Agricole ocenialiśmy każdą migrowaną aplikację, aby zdecydować, czy ją zaktualizować, czy przebudować. Jeśli potrzebują Państwo oceny własnego systemu albo zespołu, który zrealizuje plan, SDK Enterprises zapewnia jedno i drugie w ramach jednej umowy.
Najważniejsze wnioski
- Powód biznesowy modernizacji należy spisać razem z miarą, którą da się później sprawdzić.
- Kod i produkcję ocenia się razem, zanim wybierze się między aktualizacją, stopniową wymianą a przepisaniem.
- Przed zmianą struktury trzeba ustabilizować produkcję i objąć krytyczne zachowanie testami charakteryzującymi.
- System wymienia się kawałek po kawałku za warstwą routingu, chyba że przepisanie jest wyraźnie mniejsze i bezpieczniejsze.
- Własność danych, synchronizację i uzgadnianie planuje się jako osobną migrację.
FAQ
Czy przepisać starszą aplikację od zera?
Zwykle nie. Przepisywanie konkuruje z systemem, który nadal się zmienia, i wymaga ponownego odkrycia zachowań, których nikt nie udokumentował. Lepiej wymieniać system stopniowo, chyba że baza kodu jest mała i dobrze zrozumiana albo związana z platformą, której nie da się dalej utrzymywać.
Ile trwa modernizacja aplikacji legacy?
To zależy od wielkości systemu, pokrycia testami, stopnia splątania danych i zakresu zmian. Krótka ocena daje realistyczny plan i pierwszy krok na tyle mały, by go ukończyć i zmierzyć, zamiast jednej daty dla całego przedsięwzięcia.
Czy w trakcie modernizacji można dalej wydawać nowe funkcje?
Tak, i warto to robić. Stopniowa wymiana pozwala zespołowi dostarczać funkcje w nowym kodzie, podczas gdy stary system nadal działa. Trzeba uzgodnić, jaka część czasu zespołu idzie na modernizację, aby prace nad funkcjami nie pochłonęły jej po cichu.