Case study · własny produkt AOVP
To nie jest opis wdrożenia u klienta, tylko mojego własnego produktu — dlatego mogę pokazać wszystko: prawdziwe liczby z bazy, koszty utrzymania, popełnione błędy i to, co zrobiłbym dziś inaczej. Aplikacja obsłużyła cały turniej mistrzostw świata, od pierwszego gwizdka do finału.
Dane odczytane z produkcyjnej bazy 30.08.2026. Bez danych osobowych — same liczby zbiorcze.
Typowanie wyników w gronie znajomych zwykle kończy się arkuszem kalkulacyjnym albo grupą na komunikatorze. Działa przez tydzień. Potem ktoś wpisze typ po meczu, ktoś inny pomyli kolumnę, a punkty trzeba liczyć ręcznie — przy 104 meczach to kilkaset operacji.
Typ da się wpisać wyłącznie przed rozpoczęciem meczu. To brzmi banalnie, ale jest jedyną rzeczą, która sprawia, że rywalizacja ma sens — i pierwszą, której nie zapewni żaden arkusz.
Wyniki pobierane są z zewnętrznego API piłkarskiego, a punkty przeliczają się automatycznie po każdym meczu. Przez cały turniej nie wpisałem ręcznie ani jednego rezultatu.
Klasyfikacja, drabinka pucharowa, historia typów i podsumowanie sezonu. To ranking, nie tabela — i to on utrzymuje ludzi przy zabawie do finału.
14 stron aplikacji, jeden worker, zero serwerów do administrowania.
Next.js z Prismą. Renderowanie po stronie serwera, żeby ranking ładował się od razu, a nie po doczytaniu danych w przeglądarce.
D1 (SQLite Cloudflare) na dane, R2 na avatary. Baza po całym turnieju zajmuje 380 KB przy limicie 5 GB — to skala, w której koszt bazy praktycznie nie istnieje.
Pobieranie wyników, przeliczanie punktów i przypomnienia chodzą jako harmonogram wewnątrz workera. Wcześniej były to trzy wpisy w systemowym crontabie na serwerze.
Maile przez HTTP API dostawcy poczty, powiadomienia push przez Web Push — działają też na telefonie po dodaniu aplikacji do ekranu głównego.
To najbardziej użyteczny fragment tego opisu. Ładny zrzut ekranu pokaże każdy — o tym, co poszło nie tak, mało kto pisze.
Analiza zależności wciągała do paczki silniki bazodanowe dla Postgresa, MySQL-a, SQL Servera i CockroachDB — czyli sterowniki do baz, których ta aplikacja w ogóle nie używa. Po wykluczeniu niepotrzebnych plików paczka spadła z 19 151 KiB do 3 982 KiB, czyli do 39% limitu. Diagnoza zajęła kilka godzin, poprawka trzy linijki konfiguracji.
Lokalny plik z ustawieniami środowiskowymi trafiał do paczki wdrożeniowej i nadpisywał ustawienia produkcyjne — logowanie zaczęło przekierowywać na adres lokalny. Objaw wyglądał na błąd uwierzytelniania, a przyczyna siedziała w procesie budowania. Dziś skrypt budujący kasuje te pliki z gotowej paczki.
Przy przenoszeniu bazy import przerywał się na kluczach obcych, bo wiersze szły w kolejności alfabetycznej tabel — a baza sprawdza powiązania już w trakcie wczytywania. Zrzut musi być posortowany tak, żeby rekordy nadrzędne trafiały przed zależnymi. Godzina szukania, jedno zdanie rozwiązania.
Wgrywałem avatary do magazynu plików poleceniem, które domyślnie zapisuje lokalnie, nie do chmury. Sprawdzenie „czy plik się wgrał” tym samym narzędziem odczytywało tę samą lokalną kopię — i potwierdzało sukces. Błąd wyszedł dopiero jako brakujące avatary w działającej aplikacji. Wniosek, który stosuję od tamtej pory: weryfikuj przez aplikację, nie przez narzędzie, którym właśnie coś zrobiłeś.
Aplikacja przez cały turniej działała na VPS-ie za 17 zł netto miesięcznie. Po turnieju przeniosłem ją w całości na Cloudflare — nie dla oszczędności kilkunastu złotych, tylko żeby przestać administrować serwerem: aktualizacje systemu, certyfikaty, kopie zapasowe, monitoring.
Baza z pliku SQLite na serwerze → D1. Avatary z dysku → R2. Maile z SMTP → HTTP API. Trzy wpisy w crontabie → harmonogram w workerze. Sama aplikacja została ta sama.
Start workera 28 ms. Strona 0,8–1,4 s, logowanie zakończone w 0,64 s. Wcześniej odpowiadał jeden serwer w jednym miejscu — teraz najbliższa lokalizacja sieci.
Wszystkie 14 stron przetestowane po przełączeniu, komplet danych porównany co do wiersza, a 11 avatarów pobranych z działającej aplikacji i porównanych bajt w bajt z oryginałami. Pełna pętla resetu hasła sprawdzona na koncie tymczasowym.
Nie stawiałbym tego na własnym serwerze. VPS wydawał się oczywistym wyborem, bo aplikacja ma bazę danych. W praktyce oznaczał kilka miesięcy administrowania maszyną, której jedynym zadaniem było utrzymanie jednej aplikacji dla dwudziestu kilku osób. Migracja zajęła kilka dni roboczych — mniej, niż łącznie kosztowała opieka nad serwerem.
Zadania cykliczne odpalałbym rzadziej. Harmonogram chodzi co minutę, co daje ponad 43 tysiące uruchomień miesięcznie. W dniach meczowych ma to sens, poza nimi jest czystym marnotrawstwem — i przy kilku aplikacjach na jednym planie zacznie mieć znaczenie.
Wcześniej dołożyłbym logowanie awarii wysyłki. Przez długi czas nieudana wysyłka maila była całkowicie niema: kod łapał błąd i szedł dalej, więc nikt by się nie dowiedział, że powiadomienia przestały docierać. To najgroźniejszy rodzaj usterki — taki, którego nie widać.
Jeśli zastanawiasz się nad zamówieniem aplikacji, ten opis mówi o wykonawcy więcej niż portfolio z ładnymi obrazkami.
Nie makieta ani projekt koncepcyjny — 104 mecze rozliczone automatycznie i 27 osób korzystających z aplikacji przez sześć tygodni.
Potrafię powiedzieć, ile aplikacja kosztuje miesięcznie po wdrożeniu, bo sam za to płacę. To pytanie, na które wielu wykonawców odpowiada „to zależy”.
Cztery opisane wyżej pułapki kosztowały mnie czas, a nie klienta. Przy Twoim projekcie omijam je z marszu.
Typer jest dostępny jako produkt white-label — z Twoim logo, kolorami i adresem. Cennik jest na stronie Typera.
Napisz krótko, co miałaby robić — odpowiem osobiście, zwykle w ciągu jednego dnia roboczego. Pierwsza rozmowa i wstępna wycena są bezpłatne.