Jak przepisałem działającą platformę kursową — case study ckks.pl od kuchni
Przebudowa strony dla firmy, która dopiero zaczyna działalność, jest zadaniem komfortowym, ponieważ nie ma w niej czego zepsuć. Zupełnie inaczej wygląda praca przy serwisie, który od lat sprzedaje kursy w całej Polsce, ma setki podstron wypracowanych w wynikach wyszukiwania i prowadzi nabór, który nie może zatrzymać się nawet na jeden dzień.
Tak wyglądało zadanie przy ckks.pl, serwisie Centrum Kształcenia Kadr Sportowych, placówki szkoleniowej działającej od 2007 roku i prowadzącej kursy dla nauczycieli wychowania fizycznego, instruktorów i trenerów sportu oraz techników masażu w ponad trzydziestu miastach. W tym artykule opisuję od kuchni, jak przebiegało pełne przepisanie tego serwisu: od zarchiwizowania starej witryny, przez migrację treści i adresów bez utraty pozycji w wyszukiwarce, aż po nowy panel administracyjny, aplikację PWA z powiadomieniami oraz warstwę sztucznej inteligencji wspierającą codzienną pracę sekretariatu.
To druga strona mojej oferty. Nie strona w abonamencie, lecz oprogramowanie na zamówienie, czyli system projektowany pod konkretny model biznesowy. Ponieważ o takich projektach najlepiej mówić na przykładach, ten akurat można kliknąć i sprawdzić, że działa produkcyjnie.
Żywy biznes na starym silniku
Stary serwis ckks.pl przez lata dobrze wypełniał swoją rolę i zgromadził coś, czego nie da się kupić, czyli historię w wyszukiwarce Google. Pod względem technicznym opierał się jednak na rozwiązaniach, które z czasem coraz mocniej ograniczały jego rozwój.
- Adresy podstron miały postać
/zapisy-na-kurs/85/1204/Nazwa-Kursu/Miasto, a wpisane w nie identyfikatory liczbowe od dawna nikomu nie służyły. Nikt nie chciał ich jednak ruszać w obawie, że wypracowane pozycje przepadną. - Treści powstawały ręcznie w kodzie HTML. Strona prezentująca kadrę instruktorską była jednym, pielęgnowanym latami dokumentem, w którym zagnieżdżone listy przeplatały się ze stylami wpisanymi bezpośrednio w znaczniki.
- Drobne ubytki narastały latami. W mapie strony figurowały na przykład adresy kursów, które zwracały już błąd 404, co wyszło na jaw dopiero przy inwentaryzacji.
- Kampanie reklamowe kierowały ruch prosto do surowego formularza zapisu, pozbawionego treści, która mogłaby przekonać osoby jeszcze niezdecydowane.
Do tego dochodziło wymaganie, które zdefiniowało cały projekt. Widoczność w wyszukiwarce jest tutaj realnym aktywem biznesowym, ponieważ kursanci trafiają na poszczególne podstrony właśnie z Google. Każda nieprzemyślana zmiana adresu oznaczałaby zatem policzalną stratę.
Jedna baza zamiast dwóch źródeł prawdy
Najważniejsza decyzja w tym projekcie nie dotyczyła technologii, lecz granic systemu. Publiczny serwis oraz zaplecze administracyjne działają jako jedna aplikacja, oparta na jednej bazie danych. Strona kursu, jego cena, liczba wolnych miejsc na danym terminie i formularz zapisu renderują się dokładnie z tych samych rekordów, na których pracuje sekretariat, a nie z ich kopii, eksportu czy okresowej synchronizacji.
Może to wyglądać na szczegół implementacyjny, w praktyce jednak zamyka całą klasę problemów pojawiających się wszędzie tam, gdzie witryna i system obsługi trzymają własne wersje tych samych danych. Rozbieżność w rodzaju „na stronie widnieje inna cena niż w systemie” jest tutaj niemożliwa, ponieważ nie istnieją dwa miejsca wymagające uzgadniania. Kiedy pracownik zmienia termin albo zamyka nabór, publiczna strona odzwierciedla to natychmiast.
Od strony technicznej całość opiera się na frameworku SvelteKit oraz samodzielnie hostowanym backendzie Convex, na infrastrukturze, którą sam utrzymuję. Jest to ten sam fundament, na którym stoją moje pozostałe realizacje.
Najpierw archeologia, potem przeprowadzka
Zanim powstała pierwsza podstrona nowego serwisu, napisałem skrypt archiwizujący starą witrynę. Przeszedł on po mapie strony, zinwentaryzował 500 adresów, pobrał 262 dokumenty HTML oraz kilkadziesiąt plików PDF, a z surowego kodu wydobył ustrukturyzowane dane. Na tej podstawie zestaw importerów przeniósł do nowej bazy:
| Co | Ile |
|---|---|
| Kursy | 85 |
| Terminy (edycje kursów) | 1279 |
| Sylwetki prowadzących | 146 |
| Strony statyczne i kategorie | 11 + 11 |
| Aktualności | 6 |
Osobny parser przekształcił polskie zapisy dat z tysięcy terminów na format maszynowy. Objąłem go testami jednostkowymi, ponieważ pomylona data rozpoczęcia kursu nie jest zwykłą literówką, lecz oznacza czyjś nieudany wyjazd do innego miasta.
Najwięcej uwagi wymagały jednak adresy URL. Tam, gdzie dało się je zachować, nowy serwis odtwarza je co do znaku. Dotyczy to między innymi stron kategorii oraz profili prowadzących, których historyczna struktura pozostała nietknięta, aby nie stracić linków przychodzących ani wypracowanych pozycji. Tam, gdzie zmiana struktury okazała się nieunikniona, działa wygenerowana automatycznie mapa przekierowań 301, tłumacząca dawne identyfikatory liczbowe na nowe, czytelne adresy. Przy okazji świadomie połączyłem kilka bliskoznacznych podstron w jedną, tak aby ich dotychczasowa historia sumowała się na stronie kursu, zamiast rozpraszać się na konkurujące ze sobą adresy.
Do tego doszła warstwa, której użytkownik nie widzi: dane strukturalne schema.org opisujące kursy, terminy, okruszki nawigacyjne i najczęstsze pytania, dynamicznie generowana mapa strony, a także osobne podstrony kursów dla poszczególnych miast. Zapytania „kurs trenera personalnego Wrocław” oraz „kurs trenera personalnego Gdańsk” wyrażają bowiem dwie różne intencje, na które serwis powinien odpowiadać osobno przygotowaną treścią.
Co widzi kursant
Publiczna część serwisu podporządkowana została jednemu celowi, jakim jest możliwie najkrótsza droga od zainteresowania do zapisu.
- Strony kursów prezentują terminy, ceny i prowadzących, zawsze w aktualnej postaci, ponieważ pochodzą wprost z bazy, w której pracuje sekretariat.
- Sekcja „Rusza na 100%” zbiera terminy, co do których wiadomo już, że się odbędą. Odpowiada ona na pytanie, które kursanci zadają najczęściej, czyli czy dana grupa w ogóle wystartuje, i prowadzi najkrótszą drogą do formularza.
- Wyszukiwarka umieszczona w nawigacji przeszukuje na żywo kursy, miasta oraz prowadzących.
- Sam zapis przebiega w kilku krokach, na które składają się formularz zabezpieczony przed botami, płatność zaliczki online przez Przelewy24 oraz potwierdzenia mailowe na każdym etapie.
- Konto kursanta pozwala między innymi ponownie pobrać link do płatności, jeżeli wiadomość zaginęła w skrzynce.
- Formularz „zaproponuj termin”, dostępny w miastach bez aktualnego naboru, kieruje zgłoszenia wprost do panelu administracyjnego.
- Ankieta po kursie okazała się ciekawym przypadkiem archeologicznym. Poprzednia wersja działała pod tokenizowanym adresem wysyłanym mailem, przez co jej strona nie zachowała się w żadnym archiwum internetu. Odtworzyłem ją na podstawie wzoru dostarczonego przez placówkę, tym razem z wynikami zliczanymi automatycznie.
Co widzi zespół placówki
Panel administracyjny stał się w tym projekcie pełnoprawnym produktem. Składa się z dziewiętnastu sekcji obejmujących kursy i terminy, zapisy, użytkowników, media oraz statystyki. Kilka rozwiązań zasługuje na osobne omówienie.
- Pełny cykl życia zapisu. System prowadzi kursanta przez statusy płatności, od zaliczki po ewentualny zwrot, umożliwia przeniesienie go między terminami wraz z automatycznym powiadomieniem mailowym, a także rejestrowanie wpłat przyjętych poza systemem. Przy każdym zapisie utrwalana jest migawka cennika z dnia jego utworzenia, dzięki czemu późniejsza zmiana ceny kursu nie przepisuje historii.
- Historia zmian z możliwością cofnięcia. Każda modyfikacja danych w kluczowych tabelach zapisuje się automatycznie, w czytelnej formie odpowiadającej na pytania o to, kto, co i kiedy zmienił. Administrator może wycofać wprowadzoną zmianę jednym kliknięciem.
- Role zamiast wspólnego hasła. Uprawnienia rozdzielone zostały między administratora, managera, asystenta i copywritera, tak aby każdy widział dokładnie tyle, ile potrzebuje do pracy. Usunięcie danych przez osobę spoza ścisłego grona nie następuje od razu, lecz trafia do kolejki oczekującej na zatwierdzenie.
- Codzienne zaplecze SEO. Biblioteka mediów wykrywa pliki, które nie są nigdzie używane, obrazy podlegają automatycznej optymalizacji do formatu WebP, a osobny panel służy do uzupełniania opisów alternatywnych oraz nazw plików. W serwisie z setkami zdjęć jest to praca, którą ktoś musi móc wykonywać bez udziału programisty.
Powiadomienie zamiast odświeżania skrzynki
Serwis ma własną aplikację PWA, instalowaną na telefonach zespołu bezpośrednio z przeglądarki. Gdy ktoś zapisuje się na kurs, na urządzeniach pracowników pojawia się powiadomienie push zawierające nazwisko, kurs i miasto, a jedno dotknięcie otwiera właściwy zapis w panelu. Jest to drobiazg inżynierski, który realnie zmienia rytm pracy, ponieważ informacja o nowym kursancie dociera do ludzi, zamiast czekać, aż ktoś z własnej inicjatywy zajrzy do systemu.
Liczby, którym można ufać
Ta część projektu wydaje mi się najciekawsza od strony inżynierskiej. Google Analytics oraz Piksel Meta działają w serwisie zgodnie z prawem, a więc dopiero po wyrażeniu zgody na pliki cookie. W konsekwencji każde z tych narzędzi widzi wyłącznie wycinek ruchu i ich wskazania nigdy nie będą ze sobą zgodne. Dlatego serwis otrzymał dodatkowo własną analitykę, działającą bez plików cookie i bez śledzenia poszczególnych osób. Pokazuje ona lejek konwersji prowadzący od wejścia na stronę kursu przez formularz aż do zapisu, popyt w rozbiciu na miasta, listę wyszukiwań niedających żadnych wyników oraz błędy 404 wraz ze źródłem, z którego użytkownik na nie trafił.
Skuteczność kampanii reklamowych wymagała jeszcze jednego rozwiązania. Ruch pochodzący z reklam kierowanych do odbiorców nieznających placówki konwertował słabo, ponieważ trafiał na surowy formularz, natomiast strona kursu, choć zawierała komplet treści, oferowała zbyt wiele ścieżek wyjścia. Odpowiedzią okazały się dedykowane strony kampanii, wyłączone z nawigacji oraz z mapy strony, prezentujące jeden konkretny termin, treść perswazyjną i formularz w obrębie jednego widoku. Taka strona przestaje być dostępna, gdy termin się wypełni lub zdezaktualizuje, dzięki czemu kliknięcie w nieaktualną reklamę nie prowadzi już do zapisu na nieistniejącą grupę. Zdarzenia reklamowe raportowane są przy tym dwutorowo, z przeglądarki oraz z serwera, z deduplikacją po stronie odbiorczej, ponieważ blokery reklam i mechanizmy prywatności w systemie iOS gubią znaczną część zdarzeń wysyłanych z przeglądarki.
Sztuczna inteligencja, która proponuje, ale nie decyduje
W systemie pracuje również warstwa sztucznej inteligencji, od początku podporządkowana jednej zasadzie: agent proponuje, człowiek zatwierdza. Żaden tekst wygenerowany przez model nie trafia do kursanta bez decyzji pracownika, przy czym nie jest to wyłącznie deklaracja, lecz cecha konstrukcyjna systemu, w którym ścieżka wysyłki wiadomości nie istnieje bez wcześniejszego statusu „zatwierdzone”.
W praktyce sztuczna inteligencja odpowiada za dwa obszary.
- Poranny briefing. Każdego dnia, jeszcze przed otwarciem sekretariatu, system analizuje stan naborów, wychwytując nowe zapisy, duplikaty, terminy zagrożone zbyt małą liczbą uczestników oraz grupy z różnych miast, które warto rozważyć pod kątem połączenia. Z tych obserwacji powstaje krótki raport wysyłany wraz z powiadomieniem push. Istotny jest tutaj podział ról, ponieważ wykrywanie sytuacji opiera się na deterministycznych regułach liczonych na danych, a model językowy jedynie ujmuje gotowe wnioski w zwięzłą formę.
- Asystent treści. Model pomaga redagować i poprawiać polskie opisy kursów, generuje grafiki do wpisów, a osobny agent przygotowuje szkice materiałów blogowych, korzystając z wyszukiwarki oraz z treści zgromadzonych w serwisie. Każde z tych zadań kończy się na szkicu oczekującym na akceptację.
Co okazało się trudne
Zgodnie z konwencją tego cyklu opisuję również to, co nie udało się za pierwszym razem.
Migracja obrazów była realnym zagrożeniem dla startu. Jeszcze długo po zaimportowaniu treści wszystkie zdjęcia wskazywały na starą domenę. Przełączenie DNS w tym stanie oznaczałoby, że w dniu uruchomienia w całym serwisie posypałyby się grafiki. Zanim więc zmiana domeny w ogóle stanęła na porządku dziennym, musiało powstać osobne narzędzie do hurtowego przeniesienia plików i podmiany odwołań w treściach.
Część błędów znalazłem u siebie. Zdarzenia reklamowe raportowały wartość zapisu w groszach zamiast w złotych, a więc stukrotnie zawyżoną. Wyszło to przy budowie stron kampanii i zostało poprawione we wszystkich miejscach. Piszę o tym, ponieważ na tym właśnie polega pokazywanie pracy od kuchni. Błędy zdarzają się każdemu, a znaczenie ma to, czy system został zbudowany tak, by je ujawniać.
Dyscyplina zamiast siatki bezpieczeństwa. Pracuję tutaj na środowisku produkcyjnym z prawdziwymi zapisami, dlatego część zadań, jak przebudowa mechanizmu terminów, celowo zatrzymała się na etapie analizy w dokumentacji i poczeka, aż będę miał pewność, że zmiana jest bezpieczna. Nie wszystko, co zaplanowane, zostało już wdrożone. Program kodów partnerskich jest już zaprojektowany, ale świadomie czeka na swoją kolej.
Skala projektu w liczbach
| Wymiar | Wartość |
|---|---|
| Czas budowy | około czterech miesięcy (kwiecień–sierpień 2026), start produkcyjny na ckks.pl w lipcu |
| Historia zmian | 146 commitów |
| Zakres | publiczny serwis, panel obsługi i aplikacja PWA w jednym systemie |
| Baza danych | 27 tabel, w tym pełna historia zmian |
| Zmigrowana treść | 85 kursów, 1279 terminów, 146 sylwetek prowadzących |
| Ciągłość SEO | adresy zachowane lub przekierowane 301, dane strukturalne, mapa strony |
Jednej rzeczy w tym zestawieniu celowo brakuje, mianowicie wykresów wzrostu ruchu i sprzedaży. Serwis działa produkcyjnie od niedawna, a rzetelne porównanie widoczności sprzed migracji i po niej wymaga miesięcy, nie tygodni. Kiedy dane będą już dostępne, opiszę je w osobnym tekście, posługując się liczbami zamiast przymiotników.
Co z tego wynika dla Twojej firmy
Ten projekt dobrze pokazuje, czym system tworzony na zamówienie różni się od zwykłej nowej strony. Jest to przeprojektowanie sposobu, w jaki firma obsługuje swój podstawowy proces, przeprowadzone z zachowaniem tego, co udało jej się dotąd wypracować, czyli w tym przypadku pozycji w wyszukiwarce oraz nieprzerwanej ciągłości naboru. Za całość, od inwentaryzacji starego systemu po wdrożenie produkcyjne i dalszy rozwój, odpowiada jeden inżynier.
Jeżeli Twoja firma korzysta z systemu, z którego zdążyła już wyrosnąć, albo prowadzi kluczowy proces w arkuszach kalkulacyjnych i skrzynce mailowej, opowiedz mi o nim. Rozmowa nic nie kosztuje, a jej podsumowanie przeczytam osobiście. Więcej działających projektów znajdziesz w realizacjach.