
Transformacja frontendu platformy fulfillment - migracja moduł po module, bez wstrzymywania rozwoju

Cel projektu
Spłacić lata frontendowego długu technicznego w platformie, na której magazyny pracują każdego dnia, bez zamrażania produktu i bez przerywania pracy magazynu.
Zakres prac
Analiza architektury
Strategia migracji
Projektowanie UX/UI
Development frontendu
Mentoring i szkolenia AI
Czas trwania
Od 2021
Technologie
React
React Router v7
TypeScript
Vite
SWR
Klient
Omnipack
Po pięciu latach Omnipack powierzył nam platformę, na której działa biznes
Współpracę z Omnipack zaczęliśmy w 2021 roku, od ich strony internetowej. Poszło na tyle dobrze, że gdy firma postanowiła zmierzyć się z czymś znacznie poważniejszym - własną platformą fulfillment - przyszła z tym również do nas. Omnipack jest jednym z największych dostawców fulfillmentu w Polsce, więc platforma nie jest produktem pobocznym. To system, do którego logują się klienci Omnipack, żeby prowadzić swoją logistykę, i w którym personel magazynu pracuje cały dzień. To, że nam ją powierzono, jest tą częścią projektu, z której jesteśmy najbardziej dumni.

Dwa systemy, z bardzo różnymi problemami
Platforma ma dwie połowy. Client Portal to narzędzie, z którego klienci Omnipack korzystają do zarządzania produktami, stanami, dostawami, zamówieniami, zwrotami i rozliczeniami. Backoffice jest tym, z czego korzystają zespoły operacyjne i magazynowe. Obejmuje od dwudziestu do trzydziestu modułów pokrywających obsługę zwrotów, wysyłkę, zasady pakowania, inwentaryzacje, komunikację z klientem i całą resztę. Obie rosły latami na monolicie w Javie, z pomieszanymi Thymeleaf i Reactem.

Co lata nieprzerwanego wzrostu zrobiły z frontendem
Platforma była rozwijana nieprzerwanie przez lata, a backend był stopniowo modernizowany. Frontend rósł jednak bez wspólnej architektury i standardów. W efekcie działało równolegle kilka wersji Reacta, kilka bibliotek rozwiązywało te same problemy, komponenty i logika biznesowa były duplikowane, React był wymieszany z Thymeleaf, a design systemu po prostu nie było. Każdy z tych problemów da się osobno tolerować, jednak razem sprawiają, że rozwój produktu zaczyna oznaczać walkę z własnym kodem.

Część systemu istniała tylko w kodzie - bez specyfikacji, bez dokumentacji
Pod pierwszym problemem był drugi. Dokumentacja była niekompletna, więc spora część tego co platforma robiła nigdy nie została nigdzie zapisana. Żyła w kodzie i w nawykach ludzi, którzy z niej korzystali. To oznaczało, że przepisanie nie mogło zacząć się od specyfikacji, bo jej nie było.
Więc każdy moduł był rozkładany na czynniki pierwsze zanim cokolwiek zostało przebudowane
Każdy moduł przechodził dokładnie to samo. Czytaliśmy stary frontend i stojący za nim backend, mapowaliśmy każdy endpoint, śledziliśmy przepływy danych i wyszukiwaliśmy zachowania, których nikt nie udokumentował. Potem rozmawialiśmy z ludźmi, którzy z niego korzystają, bo operator magazynu wie rzeczy, których nie powie ci żadne repozytorium. Z tego powstawały makiety, warte wdrożenia usprawnienia UX i lista endpointów, które musiały powstać. Trafiało to do zespołu backendowego Omnipack, z którym wspólnie projektowaliśmy zmiany w API.

Dwie połowy, dwie strategie migracji
Client Portal został przebudowany jako jeden produkt. Migrowanie go moduł po module oznaczałoby, że klienci przeskakiwaliby między dwoma różnymi interfejsami w obrębie jednej aplikacji - a to kiepskie doświadczenie, więc cały nowy portal powstawał równolegle, ukryty za feature flagami. Pozwoliło to zespołowi bezpiecznie testować na produkcji, przełączać się między starym a nowym w każdej chwili, przenosić użytkowników stopniowo i przez cały czas utrzymywać niskie ryzyko. Backoffice poszedł odwrotną drogą, moduł po module, bo jego dwadzieścia kilka modułów obsługuje różne zespoły i każdy może funkcjonować samodzielnie. Dzięki temu wartość pojawiała się już w pierwszych tygodniach, a nie po roku; mogliśmy zaczynać od modułów, które najbardziej doskwierały, i wygaszać części starego systemu w miarę postępów. Pierwsze nowe moduły były na produkcji w ciągu miesiąca od startu.

Produkt nigdy nie przestał się rozwijać, mimo że trwała przebudowa
Migracje tego rodzaju czasami wiążą się z pewnym ryzykiem: na czas przebudowy wstrzymujesz plan rozwoju. My wykluczyliśmy to już na starcie. Nowe moduły trafiały na produkcję nieprzerwanie, a obok nich powstawały nowe funkcje biznesowe, więc Omnipack przez cały czas dostawał realną wartość, zamiast czekać na koniec migracji. Przebudowa, która zatrzymuje plan rozwoju, to przebudowa, którą biznes zaczyna po cichu kwestionować. Ta nie dała nikomu ku temu okazji.

Nowa architektura frontendu
Nowy frontend to aplikacja SPA na React Router v7, zbudowana na Vite i TypeScripcie. Jest zorganizowana wokół funkcji, więc każdy moduł stanowi osobną, samodzielną całość, zamiast rozlewać się po całej bazie kodu. Wszystko sięga do backendu wyłącznie przez API, co wytycza czystą granicę między obiema stronami. To właśnie ta granica pozwoli całkowicie wyłączyć stary frontend, gdy migracja się zakończy.

Design system, by platforma wreszcie wyglądała jak jeden produkt
Obok architektury zbudowaliśmy design system, którego platforma nigdy nie miała: kolory, typografię, tokeny projektowe, formularze, tabele, dialogi, komponenty wielokrotnego użytku i wzorce, które je spinają. Zaimplementowaliśmy go na shadcn/ui i Radix'ie, co pozwoliło nam działać szybko, zachować tożsamość wizualną Omnipack i mieć swobodę rozszerzania komponentu wszędzie tam, gdzie dany ekran wymagał czegoś customowego. Dwadzieścia modułów budowanych przez różne osoby przez kilka lat wygląda dziś, jakby należało do jednego systemu - bo należy.

Zabezpieczenia, by dług nie wracał po cichu
Spłatę długu technicznego łatwo zaprzepaścić, więc nowa baza kodu sama się przed tym broni. Linting i formatowanie egzekwuje Biome, typy są sprawdzane przy każdym commicie, nieużywany kod jest wykrywany automatycznie, a hooki pre-commit uruchamiają to wszystko równolegle, zanim cokolwiek trafi na gałąź. Konwencje są udokumentowane w repozytorium, zamiast siedzieć w czyjejś głowie.
AI w codziennej pracy, a następnie przekazane zespołowi klienta
Baza kodu tej wielkości z tak ubogą dokumentacją to dokładnie ten przypadek, w którym AI się sprawdza, więc przez cały czas korzystaliśmy z Claude'a: do czytania nieznanego kodu, mapowania endpointów, ustalania, co dany moduł naprawdę robi, generowania i refaktoryzacji kodu oraz pisania brakującej dokumentacji. Potem zaczęliśmy uczyć - prowadziliśmy mentoring dla programistów Omnipack z korzystania z tych narzędzi w realnej, codziennej pracy. Wzrost produktywności pojawił się po obu stronach - frontendu i backendu.
Jeden przydatny efekt uboczny - podłączanie naszych własnych klientów do Omnipack stało się łatwiejsze
Z tego projektu wyszło coś, czego nikt nie planował. Budujemy też sklepy e-commerce, a część z nich prowadzi fulfillment przez Omnipack. Praca przy platformie oznacza, że znamy jej API od środka, więc podpięcie do niego sklepu nie jest już kwestią czytania długiej dokumentacji. Wiemy już, co robią endpointy i gdzie kryją się trudniejsze miejsca. Integracja, która normalnie potrafi zająć sporo czasu, idzie teraz szybciej - a nasi klienci na tym korzystają, nawet nie wiedząc dlaczego poszło nam tak sprawnie.
Efekt
Client Portal został w pełni zmodernizowany. Migracja Backoffice postępuje moduł po module. Design system jest na miejscu. Architektura jest spójna, duża część długu technicznego zniknęła, wdrożenie nowego programisty zajmuje ułamek dawnego czasu, a funkcje powstają szybciej. Tempo dostarczania wzrosło do tego stopnia, że wąskim gardłem przestał być development, a stał się nim proces QA po stronie klienta.
Porozmawiajmy o Twoim projekcie!
Opowiedz nam o swoim projekcie. Pomożemy zaplanować architekturę, zakres i realizację.


