[12]
BAZARTY.COM (PAYLOAD + MONGODB)
▓ TIER B · ROK 2026 · STATUS: DZIAŁA · JĘZYKI: TYPESCRIPT
Rozbudowa zastanej bazy kodu: multi-tenant, Stripe Connect, escrow
[FIG. 1] MISJA
Umiem wejść w zastaną, dojrzałą bazę kodu i rozbudować ją bez łamania jej konwencji — a to zdarza się w pracy częściej niż greenfield. bazarty.com istnieje w dwóch architekturach — to rozbudowany wariant na Payload CMS + MongoDB (repo: marketplace-maci); wariant Supabase opisany jest osobno. Ten projekt zacząłem właśnie od cudzej, dojrzałej bazy (tutorial Antonia: multi-tenant + Stripe Connect). Najpierw zrobiłem reverse-engineering bazy do .planning/codebase/* (architektura, konwencje, integracje), a jej wzorce zamknąłem we własnych skillach Claude generujących kod zgodny z zastanymi regułami (payload-collection dla kolekcji Payload, trpc-router dla routerów tRPC). Dopiero na tym dołożyłem realny handel: czat kupujący↔sprzedawca na websocketach, pełny cykl zamówienia (potwierdzenie / spór / recenzja), maile transakcyjne, oceny, bibliotekę zakupów i ~25 stron marketingowo-prawnych. Architekturę prowadzę jako multi-tenant z izolacją danych najemców: każdy sprzedawca ma sklep na własnej subdomenie, a płatności z podziałem środków obsługuje Stripe Connect, który automatycznie zdejmuje prowizję platformy. Stack: Next.js 15 + React 19, Payload CMS + MongoDB, type-safe tRPC 11. Przegląd kodu przez AI wpiąłem w CI, żeby jakość nie zależała od mojej dyscypliny w danym dniu. Sercem platformy jest zaufanie w transakcji między nieznajomymi: własny mechanizm escrow wstrzymuje wypłatę sprzedawcy do potwierdzenia odbioru (auto-accept po 48 h, release wypłaty 2 dni później przez cron), a zwrot przy reklamacji jest zawsze pokryty. Skala: 19 kolekcji Payload, 17 routerów tRPC, ~41 tysięcy linii TypeScriptu i ponad 900 commitów — z czego zdecydowana większość to moja samodzielna rozbudowa forka.
[FIG. 2] ARCHITEKTURA
najedź na blok, by zobaczyć opis
[FIG. 3] WYZWANIA
[+][CH-01]
Rozbudowa cudzej dojrzałej bazy to inna dyscyplina niż pisanie od zera: każda moja zmiana mogła po cichu złamać konwencję, o której istnieniu nie wiedziałem. Zanim dopisałem pierwszą linijkę, zrobiłem reverse-engineering bazy do .planning/codebase/* — ARCHITECTURE, CONVENTIONS, INTEGRATIONS — a wzorce (kolekcja Payload, router tRPC) zamknąłem we własnych skillach Claude, które generują nowy kod od razu w stylu zastanego. Konwencje przestały być wiedzą plemienną — stały się egzekwowalne.
[+][CH-02]
Dla rękodzieła stock to często dokładnie 1 sztuka — a dwóch kupujących klikających „kup” w tej samej chwili nie może obaj jej dostać. Payload local API robi read-modify-write (find → sprawdź stan → update), czyli klasyczny wyścig TOCTOU: obie sesje czytają stock: 1, obie przechodzą walidację, obie dekrementują. Zamiast walczyć z frameworkiem, w tym jednym miejscu świadomie zszedłem poniżej ORM do surowego sterownika Mongo: warunkowy atomowy dekrement, w którym warunek $gte i $inc wykonują się w jednej niepodzielnej operacji — modifiedCount === 1 znaczy „zarezerwowane”, zero nadsprzedaży. Rezerwacja wpina się przed utworzeniem sesji Stripe, a rollback zwraca stan przy wygaśnięciu sesji. To jedyne miejsce w całej aplikacji omijające Payload — i jest opisane w kodzie, dlaczego.
[+][CH-03]
Escrow zbudowałem dwa razy tego samego dnia. Pierwsza wersja używała stripe'owego delay_days — wstrzymania wypłaty liczonego od płatności. Działała, ale chroniła źle: okno liczy się od zapłaty, nie od realnego odbioru, więc przy wolnej dostawie środki mogły wyjść do sprzedawcy, zanim kupujący w ogóle zobaczył paczkę. Wyrzuciłem ją po kilku godzinach i przebudowałem model: konta sprzedawców przechodzą na payouts=manual, a wypłatę zwalnia mój własny sweeper — dokładnie 2 dni po potwierdzeniu odbioru przez kupującego, z guardem idempotencji po stripePayoutId i kwotą liczoną jako lustro matematyki checkoutu, żeby nie przekroczyć salda o grosz. Gotowość wyrzucenia działającego kodu, gdy model ryzyka jest błędny — i zapisanie w historii dlaczego — uważam za najcenniejszą lekcję tego projektu.
[+][CH-04]
Integracja z Furgonetką (agregator kurierów) nauczyła mnie pokory wobec niedodokumentowanych API: tworzenie przesyłki łamało się na formacie adresu (ulica jednym polem vs rozbite street + building_number) i wersji API, a objawem dla użytkownika było „0 opcji dostawy” w checkoucie. Dwie kolejne poprawki tego samego dnia pogorszyły sprawę — obie złamały tworzenie paczki — więc trzeci commit był revertem do znanej-działającej wersji z maina, z jawnym uzasadnieniem w komunikacie. Dopiero potem przyszła właściwa naprawa: migracja na endpoint calculate-price, twarde limity wymiarów paczki i — najważniejsze — zamiana cichego zera na komunikat mówiący, czego konkretnie brakuje (np. dwuczłonowa nazwa odbiorcy, minimalne wymiary). Dyscyplina „revert do known-good zamiast nawarstwiania fixów” została mi z tego na stałe.
[FIG. 4] WARSTWA AI
AI nie siedzi w produkcie, tylko w procesie jego budowy — i to systemowo: własne skille Claude (payload-collection, trpc-router) generują kod zgodny z konwencjami zastanej bazy, agent-debugger pomaga w diagnozie, a każdy PR przechodzi przez AI code-review w CI (claude-code-review.yml). To odpowiedź na pytanie, jak skalować dyscyplinę pracy z odziedziczonym kodem.
[FIG. 2A] CI-RECENZENT
.github/workflows/claude-code-review.yml · on: pull_request
▸ commit a3f9e21feat(chat): typing indicator
[◎] CZEKAM NA PR
- ⚠ modules/chat/server: procedura tRPC bez .input() — dodaj schemat zod
- ⚠ use-chat-socket: brak cleanupu w useEffect — leak socketu przy unmount
AI-NATIVE PROCES, NIE AI-PRODUKT: REVIEW KAŻDEGO PR ROBI CLAUDE W CI
[FIG. 5] GALERIA

