Od monolitu w PHP do mikroserwisów w Go historia migracji i konkretne wskazówki technologiczne

0
5
Rate this post

Brief decyzyjny: czy problemem jest naprawdę architektura, czy raczej wdrożenia, testy, wspólna baza i brak granic w kodzie? Czy Go ma dać realną przewagę operacyjną, czy tylko zastąpić znajomy stos nowym? Czy pierwszy kandydat do wydzielenia ma własne dane, czytelny kontrakt i niezależny rytm zmian? Jak przejść z monolitu w PHP do mikroserwisów w Go bez zatrzymania rozwoju produktu i bez kosztownego „big bangu”?

Frazy pomocnicze: monolit PHP, mikroserwisy Go, migracja etapowa, strangler pattern, modularny monolit, kontrakty API, eventual consistency, observability, wydzielanie usług, granice domenowe, komunikacja PHP i Go, gotowość zespołu

Nawigacja:

1. Nie migruj architektury, dopóki nie nazwiesz właściwego problemu

Co wiemy, a czego jeszcze nie wiemy

Najdroższy błąd przy haśle „od monolitu w PHP do mikroserwisów w Go” polega na tym, że zespół leczy objawy, a nie przyczynę. Wolne release’y bywają skutkiem słabego procesu wdrożeń, nie architektury. Problemy z wydajnością często wynikają z nieoptymalnych zapytań SQL, cache’u ustawionego przypadkowo albo zbyt agresywnego ładowania danych. Z kolei napięcia między zespołami mogą wynikać z braku ownership i niejasnych granic odpowiedzialności, a nie z samego faktu istnienia monolitu.

Dobrze rozdzielić trzy klasy problemów. Pierwsza to problem wydajnościowy: wysokie zużycie CPU, blokujące zapytania do bazy, kolejki zadań rosnące szybciej niż są obsługiwane, zbyt długi czas odpowiedzi pod obciążeniem. Druga to problem organizacyjny: jedna zmiana wymaga koordynacji kilku osób, release jest możliwy tylko raz na tydzień, a każda poprawka przechodzi przez długi łańcuch zależności. Trzecia to problem architektoniczny: brak granic domenowych, kod płatności odwołuje się do modułu promocji, a raporty czytają tabele, których formalnie nie powinny znać.

Co wiemy? Zwykle to, gdzie boli najbardziej: wdrożenia, awarie po zmianach, trudność testowania, niestabilne integracje, przeciążona baza. Czego nie wiemy? Który z tych problemów zniknie po wydzieleniu usług, a który tylko zmieni formę. Jeśli dziś zespół nie mierzy czasu wdrożenia, liczby rollbacków, średniego czasu naprawy i liczby modułów dotkniętych jedną zmianą, to decyzja o mikroserwisach powstaje bardziej z intuicji niż z faktów.

Sygnały mylące, które popychają w złą stronę

Monolit w PHP bardzo często bywa obwiniany za wszystko. Tymczasem częsty obraz jest prostszy: testy end-to-end trwają zbyt długo, bo obejmują cały system; migracje bazy danych wykonywane są ręcznie; pipeline CI uruchamia pełny zestaw kontroli nawet dla kosmetycznej zmiany; cache aplikacyjny maskuje problemy tylko do momentu większego ruchu. W takim układzie podział na mikroserwisy nie rozwiązuje sedna, a dokłada nowe warstwy operacyjne.

Inny mylący sygnał to lokalne przeciążenie jednego obszaru systemu. Jeśli aplikacja e-commerce działa poprawnie, ale endpoint do importu ofert od partnerów obciąża procesy workerów, to rozwiązaniem nie musi być rozbicie całego systemu. Często wystarcza wydzielenie jednego modułu integracyjnego albo osobnych kolejek. Zamiast rozpoczynać szeroką migrację, lepiej najpierw sprawdzić, czy problem da się odseparować mniejszym ruchem.

Bywa też odwrotnie: wydajność nie jest głównym bólem, ale każda zmiana biznesowa uruchamia lawinę zmian technicznych. Przykład typowy: zmiana w płatnościach wymaga poprawki w zamówieniach, historii zdarzeń, panelu administracyjnym, fakturowaniu i webhookach. Monolit może odpowiadać szybko, ale koszt zmiany jest nieproporcjonalnie wysoki. Wtedy migracja do hybrydy albo mikroserwisów ma sens nie dlatego, że PHP jest za wolne, tylko dlatego, że sprzężenia kosztują zbyt dużo.

Krótka diagnostyka przed pierwszą decyzją

Zanim pojawi się plan wydzielania usług, opłaca się przejść przez prostą listę pytań kontrolnych:

  • Czy problemem jest release? Jak długo trwa od merge do produkcji?
  • Czy problemem jest skala? Które moduły wymagają osobnego skalowania?
  • Czy problemem jest zmiana? Ile miejsc dotyka jedna funkcja biznesowa?
  • Czy problemem jest baza? Które obszary są spięte wspólnymi transakcjami?
  • Czy problemem jest odpowiedzialność? Czy wiadomo, kto odpowiada za dany obszar od kodu po utrzymanie?

Jeżeli odpowiedzi wskazują głównie na proces i jakość inżynieryjną, porządkowanie monolitu może dać większy zwrot niż migracja. Jeżeli dominują zależności domenowe, brak możliwości niezależnych wdrożeń i potrzeba osobnego skalowania wybranych obszarów, wtedy migracja z monolitu PHP do mikroserwisów w Go staje się racjonalną hipotezą, a nie modą.

2. Oceń uczciwie, czy mikroserwisy są rozwiązaniem, czy tylko nową etykietą

Kiedy to ma sens

Mikroserwisy mają sens wtedy, gdy istnieją wyraźne granice domenowe i realna potrzeba niezależności. Klasyczny przypadek to system, w którym powiadomienia, rozliczenia, import danych, raportowanie i panel administracyjny rozwijają się w różnym tempie. Jeżeli moduł integracji z partnerami wymaga częstych zmian i innego cyklu release niż rdzeń zamówień, wydzielenie tej części przestaje być teorią architektoniczną, a zaczyna być ruchem organizacyjno-technicznym.

Drugi mocny sygnał to różny profil obciążenia. Monolit PHP często bywa wystarczający dla operacji CRUD i klasycznych ścieżek webowych. Problem zaczyna się tam, gdzie jeden obszar systemu jest silnie I/O-heavy: odbiera webhooki, konsumuje kolejki, wykonuje wiele równoległych połączeń do zewnętrznych API, przetwarza pliki albo agreguje dane. Wydzielona usługa w Go może wtedy zużywać zasoby bardziej przewidywalnie i dać łatwiejsze skalowanie poziome.

Trzeci argument to niezależne wdrożenia. Jeżeli zmiana w module wysyłki wiadomości nie powinna czekać na pełny release całego monolitu, a dziś czeka, to architektura blokuje proces. W takim układzie jedna lub kilka usług obok rdzenia może przynieść zauważalną poprawę nawet bez pełnego przejścia na mikroserwisy.

Kiedy lepiej się wstrzymać

Jeśli zespół jest mały, nie ma sensownego CI/CD, nie utrzymuje monitoringu i nie ma praktyki on-call, to mikroserwisy będą głównie mnożeniem punktów awarii. W monolicie jedna awaria jest trudna. W kilku usługach bez observability każda awaria jest trudniejsza, bo trzeba ustalić nie tylko co się zepsuło, ale także gdzie i po jakiej ścieżce.

Drugie ostrzeżenie dotyczy danych. Jeżeli niemal cały system opiera się na jednej silnie transakcyjnej bazie i większość funkcji biznesowych zakłada wspólny commit na wielu tabelach, to przejście do mikroserwisów bez przebudowy modelu danych kończy się zwykle półśrodkiem: osobne repozytoria kodu, ale jedna baza i zależności ukryte głębiej niż wcześniej. To nie jest realna dekompozycja, tylko nowa warstwa organizacyjnego kosztu.

Trzecia sytuacja to brak dojrzałości granic w samym monolicie. Jeżeli dziś moduły w PHP nie mają czytelnych interfejsów, logika domenowa jest rozlana po kontrolerach, serwisach, listenerach i zapytaniach SQL, to wydzielanie usług od razu przenosi chaos na sieć. W takiej sytuacji modularny monolit jest często etapem koniecznym, nie zbędnym.

Trzy rozsądne ścieżki zamiast jednego hasła

ŚcieżkaKiedy pasujeZaletyRyzyka
Porządny monolitProblemy są głównie procesowe lub jakościoweNajniższa złożoność operacyjna, szybkie porządkiOgraniczona niezależność wdrożeń i skalowania
Modularny monolitGranice domenowe są możliwe, ale dane nadal mocno wspólneLepsza separacja bez kosztu sieci i wielu środowiskPozorna modularność, jeśli brak dyscypliny architektonicznej
Hybryda z kilkoma usługamiNiektóre obszary mają inny rytm rozwoju lub skalowaniaKontrolowany zysk przy ograniczonym ryzykuOkres przejściowy PHP + Go wymaga dobrych kontraktów

W praktyce najrozsądniejsza bywa hybryda: rdzeń zostaje w monolicie lub modularnym monolicie, a obszary naturalnie odrębne przechodzą do usług. To ważne, bo zatrzymanie się przed pełnymi mikroserwisami nie jest porażką. Czasem jest najlepszą możliwą decyzją.

3. Wybieraj Go z powodów operacyjnych, nie wizerunkowych

Gdzie Go ma praktyczną przewagę

Go bywa wybierane przy wydzielaniu usług z monolitu nie dlatego, że „zastępuje PHP”, ale dlatego, że dobrze sprawdza się w określonym typie zadań. Najczęściej chodzi o lekkie usługi API, worker’y kolejkowe, przetwarzanie webhooków, integracje z wieloma zewnętrznymi systemami oraz komponenty infrastrukturalne. Tam przewaga wynika z prostego modelu budowania binarek, sensownej współbieżności i przewidywalnego zużycia pamięci.

Jeżeli usługa ma odbierać tysiące krótkich żądań, czekać na odpowiedzi z zewnętrznych API, obsługiwać timeouty i przekazywać dane dalej, Go jest praktycznym wyborem. Podobnie w przypadku procesów asynchronicznych: konsumentów kolejek, schedulerów, usług retry, dedykowanych walidatorów plików czy serwisów notyfikacyjnych. Tego typu komponenty zwykle mają prostszy model domenowy, ale dużo operacji sieciowych i współbieżności.

Dodatkowym argumentem jest wygoda operacyjna. Pojedyncza binarka, prosty deployment i niewielka zależność od środowiska wykonawczego upraszczają uruchamianie oraz skalowanie. Dla zespołu operacyjnego ma to większe znaczenie niż marketingowe hasła o „nowoczesnym stosie”.

Gdzie nie warto go forsować

Nie każda część systemu zyskuje na przepisaniu do Go. Jeśli logika biznesowa jest mocno osadzona w dojrzałym frameworku PHP, wykorzystuje bogaty ekosystem bibliotek domenowych, a zespół ma lata doświadczenia w tym stosie, to koszt migracji może zjeść przewagę techniczną. Dotyczy to zwłaszcza skomplikowanych obszarów biznesowych, gdzie najtrudniejsze nie jest wystawienie API, tylko utrzymanie poprawnej logiki i szybkości zmian.

Problemem może być też gotowość zespołu. Go jest językiem stosunkowo prostym składniowo, ale prostota języka nie usuwa potrzeby ustalenia standardów: obsługi błędów, struktury pakietów, loggera, middleware, testów integracyjnych, kontraktów, sposobu budowania klienta HTTP czy podejścia do kontekstu i timeoutów. Jeśli zespół nie ma czasu na wypracowanie takich zasad, nowy język zaczyna generować dług architektoniczny od pierwszego sprintu.

Niekiedy lepsza decyzja to pozostawienie części wydzielanych usług również w PHP, szczególnie gdy celem jest przede wszystkim separacja odpowiedzialności i niezależny deploy. Mikroserwisy nie wymagają automatycznie zmiany języka. Go ma sens tam, gdzie charakter usługi uzasadnia ten wybór.

Praktyczne kryteria wyboru Go

  • Wybieraj Go, gdy usługa jest sieciowa, I/O-heavy, ma dużo współbieżności lub działa jako worker.
  • Zastanów się dwa razy, gdy logika domenowa jest złożona i silnie osadzona w obecnym frameworku PHP.
  • Wstrzymaj zmianę języka, jeśli zespół nie ma jeszcze standardów CI/CD, observability i testów kontraktowych.
  • Nie licz na sam język, jeśli głównym problemem są słabe granice domenowe albo wspólna baza.

Przykład praktyczny: serwis powiadomień, który przyjmuje zdarzenia, dobiera kanał, pilnuje retry i statusów dostarczeń, jest naturalnym kandydatem do Go. Moduł wyceny złożonych promocji, głęboko związany z istniejącymi modelami zamówień i regułami biznesowymi, już znacznie mniej.

4. Pierwszy serwis wybierz po granicach domenowych, a nie po prestiżu modułu

Cechy dobrego pierwszego kandydata

Pierwszy serwis ma przede wszystkim obniżyć ryzyko. Powinien mieć czytelny zakres odpowiedzialności, niski coupling względem reszty systemu i sensowny model komunikacji. Dobrze, jeśli jego dane mają wyraźnego właściciela i nie wymagają rozbudowanych wspólnych transakcji z rdzeniem. Taki kandydat daje szansę zbudować standardy migracji bez paraliżu produktu.

Dobrym sygnałem jest osobny rytm skalowania. Jeśli jeden moduł bywa przeciążony w inny sposób niż reszta aplikacji, łatwiej uzasadnić wydzielenie. Podobnie wtedy, gdy obszar wykonuje dużo operacji zewnętrznych: woła partnerów, kolejki, systemy mailowe, SMS, push, narzędzia raportowe. Tam granice techniczne i biznesowe częściej pokrywają się sensownie.

Przy wyborze pierwszej usługi pomocne są krótkie pytania kontrolne:

  • Czy moduł ma jednego czytelnego właściciela biznesowego i technicznego?
  • Czy można opisać jego odpowiedzialność jednym zdaniem, bez długiej listy wyjątków?
  • Czy dane tego obszaru da się wydzielić bez utrzymywania wspólnego commita z połową systemu?
  • Czy awaria tej funkcji nie zatrzyma całej sprzedaży, logowania albo krytycznej ścieżki użytkownika?
  • Czy zespół umie już wskazać kontrakt wejścia i wyjścia: API, zdarzenia, retry, błędy, timeouty?

Jeśli na większość tych pytań odpowiedź brzmi „nie wiemy”, to sygnał ostrzegawczy. Co wiemy? Że system jest trudny. Czego nie wiemy? Gdzie naprawdę przebiegają granice. W takiej sytuacji lepiej najpierw uporządkować moduł w monolicie: odseparować logikę, nazwać interfejsy, ograniczyć bezpośrednie odwołania do cudzych tabel. Dopiero potem podejmować decyzję o wyjściu za sieć.

Złe pierwsze wybory zdarzają się częściej niż dobre

Kuszące bywa wydzielenie modułu „najważniejszego”, bo wtedy zmiana wygląda strategicznie. W praktyce to często najgorszy ruch. Płatności, koszyk, autoryzacja, pricing albo rdzeń zamówień zwykle mają najwięcej wyjątków, zależności i ukrytych powiązań z danymi. Wydzielenie takiego obszaru na starcie zamienia migrację w program naprawczy całego systemu, a nie kontrolowany eksperyment architektoniczny.

Znacznie lepiej sprawdzają się obszary poboczne, ale pełne operacyjnie: notyfikacje, webhooki, eksporty, generowanie dokumentów, asynchroniczne integracje z partnerami, czasem wyszukiwarka. Taki serwis nadal uczy zespoł standardów komunikacji między PHP i Go, ale nie stawia od razu całego biznesu na jednej decyzji. To różnica między testem granic a hazardem.

Przykład z typowej praktyki: moduł wysyłki e-maili i SMS-ów bywa dobrym kandydatem, bo ma własne retry, własne statusy i dużo wywołań zewnętrznych. Ten sam system może mieć jednocześnie fatalnego kandydata w postaci kalkulacji rabatów, jeśli reguły promocji są porozrzucane po kilku warstwach PHP i zależą od wspólnego stanu zamówienia.

Rozsądny kolejny krok jest prosty: wybierz jeden obszar z czytelną granicą, opisz kontrakt, zmierz zależności i dopiero wtedy decyduj, czy potrzebujesz mikroserwisu, modularnego monolitu, czy po prostu lepiej uporządkowanego PHP. Architektura zaczyna pomagać dopiero wtedy, gdy odpowiada na konkretny problem, a nie wtedy, gdy tylko dobrze wygląda na diagramie.

5. Migrację prowadź etapami: strangler pattern, adaptery i cienka warstwa przejściowa

Najdroższy błąd to próba przepisania całego obszaru naraz. Brzmi ambitnie, ale zwykle kończy się długim okresem podwójnej logiki, opóźnieniami i sporami o to, który system jest źródłem prawdy. Bezpieczniejszy wariant jest prostszy: najpierw odetnij wejścia, potem stopniowo przekierowuj ruch i dopiero na końcu wycinaj stary kod.

Jak wygląda to w praktyce

Strangler pattern nie polega na „pisaniu nowego obok starego” bez planu. Chodzi o kontrolowane przejmowanie odpowiedzialności przez nową usługę. Monolit nadal działa, ale część wywołań przechodzi przez warstwę pośrednią: adapter, fasadę albo bramkę aplikacyjną. Dzięki temu decyzja o przełączeniu nie jest zaszyta w kilkunastu kontrolerach i trzech cronach.

  • Krok 1: ustal punkt wejścia — jedno miejsce, przez które monolit komunikuje się z wydzielanym obszarem.
  • Krok 2: wprowadź adapter — dziś woła kod lokalny, jutro może wołać usługę HTTP lub publikować zdarzenie.
  • Krok 3: uruchom nowy serwis obok starego — najpierw bez pełnego ruchu produkcyjnego.
  • Krok 4: przełączaj scenariusze stopniowo — po typach żądań, klientach, regionach albo feature flagach.
  • Krok 5: usuń martwą ścieżkę — dopiero gdy logi, metryki i obsługa błędów potwierdzą stabilność.

Praktyczny sens jest prosty: ryzyko rozkłada się na serię małych decyzji. Jeśli nowa usługa nie domyka wymagań, można się wycofać bez awarii całego modułu.

Nie buduj grubej warstwy przejściowej

Warstwa pośrednia ma pomagać w migracji, a nie stać się nowym mini-monolitem. To częsty problem: adapter zaczyna mapować dziesiątki wyjątków, scala dane z kilku miejsc i zawiera coraz więcej reguł biznesowych. Po kilku miesiącach zespół utrzymuje już trzy byty: stary kod PHP, nową usługę Go i „tymczasową” logikę między nimi.

Dobra warstwa przejściowa powinna robić niewiele: autoryzować, tłumaczyć kontrakt, dodawać timeouty, obsłużyć retry tam, gdzie to uzasadnione, i emitować metryki. Jeśli zaczyna podejmować decyzje domenowe, to znak, że granica została źle narysowana.

Krótki przykład: moduł generowania dokumentów można schować za interfejsem DocumentService. Najpierw wywołuje klasę w PHP, później klienta HTTP do Go. Kontrolery i joby nie wiedzą, co jest pod spodem. To dobra zmiana. Zła zmiana zaczyna się wtedy, gdy ten sam adapter sam ustala szablony, rabaty, walidacje i obieg akceptacji.

6. Kontrakty między PHP i Go zaprojektuj tak, jakby miały przetrwać kilka lat

Okres przejściowy zwykle trwa dłużej, niż zakłada plan. Dlatego kontrakt między monolitem a usługą nie może być „tymczasowym JSON-em”, który później się poprawi. To właśnie tu najczęściej pojawiają się koszty ukryte: niejawne pola, brak wersjonowania, różne interpretacje błędów i timeoutów.

Co powinno być ustalone od początku

  • Semantyka operacji — kto za co odpowiada i co oznacza sukces albo częściowe powodzenie.
  • Wersjonowanie — niekoniecznie skomplikowane, ale jawne. Zmiana kontraktu bez planu migracji szybko psuje oba systemy.
  • Idempotencja — szczególnie dla webhooków, płatności, notyfikacji i jobów retry.
  • Timeouty i retry — osobno dla wywołań synchronicznych i osobno dla kolejek czy eventów.
  • Kody błędów — biznesowe i techniczne nie powinny mieszać się w jeden komunikat typu 500.
  • Tracing i correlation ID — bez tego diagnoza problemów między PHP i Go będzie zgadywaniem.

Co wiemy, gdy kontrakt jest słaby? Że „działa u nas”. Czego nie wiemy? Jak zachowa się po opóźnieniach sieci, dubletach wiadomości i częściowych awariach partnerów. To nie detal implementacyjny, tylko koszt utrzymania w czystej postaci.

HTTP czy zdarzenia?

Nie ma jednej właściwej odpowiedzi. Wywołania synchroniczne są prostsze do zrozumienia i dobre tam, gdzie użytkownik czeka na odpowiedź. Zdarzenia i kolejki lepiej sprawdzają się przy procesach, które mogą zakończyć się później: wysyłka powiadomień, eksport, synchronizacja z partnerem, przeliczanie indeksów wyszukiwarki.

Rozsądna reguła jest taka: jeśli wynik musi wrócić od razu do użytkownika lub kolejnej warstwy biznesowej, użyj dobrze opisanego API. Jeśli proces może zostać odłożony, rozważ komunikację asynchroniczną i jawny model statusów.

Zespół prezentuje rozwiązania technologiczne w nowoczesnym biurze
Źródło: Pexels | Autor: Mikhail Nilov

Typowy błąd to udawanie asynchroniczności przez synchroniczne wywołanie, które i tak trwa długo, bo pod spodem czeka na kilka zewnętrznych systemów. Lepszy model bywa prostszy: przyjęcie żądania, zapis stanu, publikacja zdarzenia i osobny endpoint do sprawdzenia statusu.

7. Dane rozdzielaj ostrożnie: wspólna baza przyspiesza start, ale spowalnia wyjście

Najbardziej problematyczny moment migracji zwykle nie dotyczy API, tylko danych. Dopóki dwa światy współdzielą tabele i transakcje, dopóty niezależność jest ograniczona. Z drugiej strony zbyt szybkie cięcie bazy może rozsadzić procesy biznesowe, które opierają się na wspólnym stanie.

Praktyczna kolejność decyzji

Najpierw ustal właściciela danych. Każdy wydzielany obszar powinien mieć jasno wskazane encje, za które odpowiada. Potem sprawdź, kto je tylko odczytuje, a kto naprawdę modyfikuje. To robi dużą różnicę, bo read-model można czasem skopiować lub odtworzyć zdarzeniami, ale współdzielony zapis szybko prowadzi do konfliktów.

  • Na starcie dopuszczalne bywa czytanie ze starej bazy, jeśli to etap przejściowy i ma datę końcową.
  • Unikaj wspólnego zapisu z dwóch stron do tych samych tabel, bo odpowiedzialność staje się nieczytelna.
  • Osobna baza ma sens, gdy usługa realnie przejmuje ownership nad własnym stanem.
  • Eventual consistency trzeba nazwać wprost, a nie traktować jako przypadkowe opóźnienie replikacji.

Transakcje rozproszone rzadko są dobrym pierwszym wyborem

Jeśli wydzielany proces wymaga jednoczesnego commita w monolicie i nowej usłudze, to zwykle znaczy, że granica jest jeszcze niedojrzała albo przepływ wymaga przeprojektowania. W praktyce lepiej sprawdzają się lokalne transakcje po każdej stronie i komunikacja zdarzeniowa z mechanizmem kompensacji tam, gdzie to konieczne.

To nie znaczy, że wszystko można „załatwić eventami”. Oznacza tylko tyle, że spójność danych trzeba dobrać do procesu. Status notyfikacji może być finalizowany asynchronicznie. Finalizacja zamówienia z ograniczonym stanem magazynowym wymaga już większej ostrożności i często nie jest dobrym kandydatem na pierwszy etap wydzielenia.

Krótki przykład z praktyki: eksport dokumentów do partnera zniesie opóźnienie i ponowne przetworzenie. Aktualizacja limitu kredytowego klienta już niekoniecznie. Jeśli krytyczny proces nie toleruje opóźnień, duplikatów ani ręcznej kompensacji, nie zakładaj z góry, że nowa usługa uprości sprawę.

8. Observability i bezpieczeństwo potraktuj jak warunek wejścia, nie etap „po migracji”

Monolit bywa trudny, ale przynajmniej zwykle ma jedno miejsce, w którym można szukać problemu. Po wydzieleniu usług pojawia się sieć, timeouty, ponowienia, zależności od brokerów i zewnętrznych API. Bez sensownych logów, metryk i trace’ów diagnoza awarii wydłuża się natychmiast.

Minimalny zestaw, bez którego nie ma sensu ruszać

  • Ustrukturyzowane logi z identyfikatorem żądania i kluczowym kontekstem biznesowym.
  • Metryki techniczne: latency, error rate, throughput, saturation.
  • Distributed tracing między PHP, Go i zależnościami zewnętrznymi.
  • Health checki i readiness sensownie opisujące stan usługi.
  • Jawne timeouty po obu stronach, bez domyślnego „czekaj w nieskończoność”.
  • Sekrety, autoryzacja usług i audyt — szczególnie gdy nowy serwis dotyka danych klientów lub operacji finansowych.

To nie jest „infrastrukturalny luksus”. Jeśli usługa działa bez trace’ów i bez ustalonych timeoutów, pierwszy incydent produkcyjny szybko pokaże, ile kosztuje pozorna oszczędność. Ten koszt wraca zwykle w nocy, przy on-callu.

Jak mierzyć, czy migracja pomaga

Nie tylko przez CPU i czas odpowiedzi. Trzeba patrzeć szerzej: lead time zmian, liczba incydentów, czas diagnozy, częstotliwość deployów, zakres rollbacków, zależność między zespołami. Jeżeli po wydzieleniu jednej usługi dwa zespoły czekają na siebie dłużej niż wcześniej, to architektura nie poprawiła przepływu pracy, tylko go przeorganizowała.

Dobre sygnały są dość konkretne: krótszy czas wdrażania zmian w danym obszarze, mniejszy blast radius awarii, łatwiejsze skalowanie wybranego procesu i mniej przypadków, w których modyfikacja jednego modułu psuje niepowiązany fragment systemu.

9. Zmiana architektury bez zmiany odpowiedzialności zespołu zwykle nie działa

Mikroserwisy nie porządkują organizacji automatycznie. Jeśli nadal nikt nie ma pełnego ownership nad usługą, jej kontraktem, testami, deployem i reakcją na incydenty, to zespół tylko rozłoży ten sam chaos na większą liczbę repozytoriów.

Jakie zmiany organizacyjne są naprawdę potrzebne

  • Jasny owner usługi — techniczny i, jeśli to możliwe, produktowy.
  • Samodzielny pipeline CI/CD dla wydzielanego obszaru.
  • Testy kontraktowe i integracyjne zamiast opierania się wyłącznie na testach manualnych.
  • On-call lub przynajmniej czytelny model wsparcia dla awarii w nowej usłudze.
  • Standardy inżynierskie wspólne dla usług — logowanie, błędy, struktura endpointów, polityka wersjonowania.

Jeżeli nowa usługa trafia do „wspólnej odpowiedzialności wszystkich”, zwykle oznacza to odpowiedzialność niczyją. To jeden z powodów, dla których część zespołów rozsądnie zatrzymuje się na modularnym monolicie lub hybrydzie. Gdy struktura organizacyjna nie wspiera niezależnych usług, koszt koordynacji zaczyna przewyższać zysk z podziału.

Najbardziej praktyczny następny krok nie musi być spektakularny: wybrać jeden kandydacki obszar, rozpisać jego granice danych i kontrakt komunikacji, a potem sprawdzić, czy zespół potrafi go samodzielnie wdrożyć, monitorować i utrzymać. Jeśli odpowiedź jest niejasna, problem prawdopodobnie nie leży jeszcze w braku mikroserwisów.

10. Zatrzymaj migrację, jeśli pierwsze wyniki nie poprawiają przepływu pracy

Najdroższy błąd nie polega na tym, że pierwszy serwis wyjdzie niedoskonale. Kosztowniej jest iść dalej mimo sygnałów, że zespół produkuje nową złożoność szybciej niż realne korzyści. Mikroserwisy nie są strategią „raz uruchomionej” migracji. Po każdym etapie trzeba sprawdzić, co faktycznie się poprawiło.

Sygnały, że kierunek ma sens

  • Zmiany w wydzielonym obszarze wdrażają się szybciej niż wcześniej w monolicie.
  • Awaria nowej usługi nie zatrzymuje całego systemu, tylko ogranicza konkretną funkcję.
  • Zespół umie samodzielnie diagnozować problemy bez przekopywania się przez kilka warstw „czy to jeszcze PHP, czy już Go”.
  • Skalowanie wybranego procesu stało się prostsze, bo obciążony komponent da się rozwijać niezależnie.

Sygnały alarmowe, przy których lepiej zwolnić

  • Każda zmiana wymaga synchronizacji kilku zespołów, choć wcześniej robił to jeden.
  • Nowe API jest obchodzone bezpośrednim dostępem do starej bazy, bo inaczej „się nie da”.
  • Incydenty są częstsze, a diagnoza trwa dłużej mimo dodania nowej infrastruktury.