Kraków / zdalnie · otwarty na propozycje

Marcin RapaczSenior Frontend Developer / AI Engineer

Frontend developer, sześć lat przy jednym produkcie. Od 2026 głównie aplikacje z warstwą AI, a coraz częściej wychodzę też poza sam frontend: piszę backend i doprowadzam projekty aż do wdrożenia na serwerze.

LinkedIn ↗

01Doświadczenie

05.2026 — obecnie

Fullstack Developer / AI Engineer

Nodeic — własna działalność (kontrakt B2B) · klient: platforma rekrutacyjna AI (NDA)

Rozwój platformy rekrutacyjnej z wyszukiwarką kandydatów opartą o AI. Frontend zbudowałem od zera; backend, baza wektorowa i infrastruktura już działały, więc pracuję w istniejącym kodzie — silnik wyszukiwania, model danych, migracje i przetwarzanie całej bazy po zmianach. Dla tego samego klienta powstało też wewnętrzne narzędzie do pozyskiwania kontaktów.

  • Zbudowałem od zera produkcyjną aplikację webową (Nuxt 4 / Vue 3, SSR, BFF): architektura Vertical Slice na Nuxt Layers, własny design system, i18n PL/EN, analityka z consent mode.
  • Rozwijam silnik wyszukiwania semantycznego kandydatów (Google Gemini, embeddingi e5-large, Qdrant) na korpusie kilku tysięcy CV: przebudowa na wzorzec strategii, twarde filtry, wersjonowanie potoku i masowa re-ekstrakcja na produkcji. Drugą wersję silnika pisałem w TDD — silnik wyliczeń otestowany w całości, a brzegowe odpowiedzi modelu sprawdza atrapa LLM, bo prościej napisać test, niż zmusić model do konkretnej złej odpowiedzi.
  • Poprawiłem jakość danych, na których stoi wyszukiwanie: kanoniczna kategoria zawodu wyznaczana przy ekstrakcji CV, sprowadzenie zapisów umiejętności do wspólnej postaci i przebudowa dopasowania z obsługą polskiej fleksji. zobacz case study ↓
  • Ustanowiłem metodologię ewaluacji wyszukiwania (LLM-as-judge, korelacje rankingowe, benchmark 50 ogłoszeń zbudowany z logów realnego ruchu) i na niej oparłem decyzje: naprawa danych podniosła zgodność rankingu z niezależną oceną z 2% do 52%, R&D rerankingu cross-encoderem zakończyło się udokumentowaną decyzją o niewdrażaniu z wnioskiem, że następny skok da dopiero reranking LLM, a zbudowana w izolacji od produkcji druga wersja silnika — z lekkim rerankerem LLM zbudowanym na tym wniosku — wygrywa na benchmarku 22:2 (zgodność kolejności 70% wobec 35%) i obsługuje ruch produkcyjny — bilans po wdrożeniu, liczony na 1214 wierszach logów realnych wyszukiwań, wykazał cztery klasy defektów zdjęte z listy klienta, w tym filtr wycinający ponad połowę puli w 9% zapytań i 6% pozycji z wynikiem poniżej progu jakości. zobacz case study ↓
  • RODO w warstwie AI: fail-closed anonimizacja nazwisk w tekście generowanym przez LLM (z obsługą polskiej deklinacji). Samodzielnie wykryłem i załatałem stored XSS w podglądzie CV: plik wracał z typem zapisanym przy uploadzie i otwierał się jako blob dziedziczący origin panelu — serwer wymusza dziś typ treści, dokłada nosniff i odrzuca wszystko, co nie jest PDF-em.
  • Zbudowałem dla tego samego klienta drugą wersję narzędzia do pozyskiwania leadów B2B (Python, Pydantic AI / Gemini, FastAPI, PostgreSQL, Docker), napisaną od nowa: potok od ogłoszeń o pracę, przez profile pracodawców i wzbogacanie modelem z groundingiem, po trzy niezależne źródła weryfikacji telefonu — strona firmy, rejestr GUS, Google Places — w kolejności „darmowe przed płatnym”. Każdy fakt ma pochodzenie i status, model działa w jednym typowanym wywołaniu z zakazem zgadywania, a jego wynik zasila deterministyczny filtr targetu. W bazie ok. 1,3 tys. firm, 83% firm w targecie z potwierdzonym telefonem. zobacz case study ↓

03.2020 — 05.2026

List referencyjny ↗

Frontend Developer

Printbox Sp. z o.o.

Firma produktowa z własnymi edytorami personalizacji foto-produktów i setkami klientów na świecie. Sześć lat w zespole e-commerce: od redesignów mobilnych wersji edytorów w React, przez budowę headless frontendu na Shopware od demo do produkcji, po architekturę multi-tenant i migrację frameworka na żywym produkcie.

  • Współtworzyłem architekturę multi-tenant dla trzech tierów klientów i odpowiadałem za jej pełną implementację — od konfiguracji w panelu po pełny redesign (Turborepo, Nuxt Layers, podział komponentów UI/logika, plugin konfiguracji funkcji): nowe sklepy powstają nad wspólnym core bez forka i bez regresji u pozostałych.
  • Odpowiadałem za migrację z Nuxt 2 na Nuxt 3 na żywym produkcie — inne osoby dołączały, gdy miały wolne przebiegi: przeniesienie wspólnego dema i nowy system budowania; przy okazji przeniosłem konfigurację do Redisa i zacząłem ją czytać w runtime, co skończyło rozjazdy danych między instancjami i przebudowy aplikacji po każdej zmianie w panelu. zobacz case study ↓
  • Przygotowałem frontend z integracjami przy wdrożeniu najważniejszego klienta platformy, amerykańskiej sieci retail: pełny redesign, SSO i nowa bramka płatności we współpracy z backendem. Przy pilotażu globalnego producenta elektroniki, wchodzącego na rynek foto, zbudowałem większość frontendu. zobacz case study ↓
  • Osadziłem flagowy edytor foto-produktów (React) w sklepach Nuxt — pogodzenie dwóch routerów, nawigacja przeglądarki, języki konfigurowane w runtime; dla renderowanych po stronie klienta ścieżek zakupowych zbudowałem prerender na Cloudflare Workers (SEO, oszczędność kilkuset USD/mies.). zobacz case study ↓
  • Przebudowałem system budowania i wdrożeń: jeden obraz Dockera konfigurowany zmiennymi środowiskowymi zamiast builda per klient, odchudzony z 1,4 GB do 55 MB — aktualizacja wszystkich sklepów w minuty zamiast godzin, niezależnie od ich liczby.
  • Zintegrowałem płatności (Stripe, Adyen) i dostawy (InPost, GLS) w krytycznej ścieżce zakupowej, w środowisku legacy pluginów Shopware.
  • Codziennie z backendem, a z produktem przed zadaniem: konsultacje, co da się zrobić lepiej, taniej albo szybciej, planowanie i estymacje zadań oraz redesignów. Często to ja zagłębiałem nowe rozwiązania, w które warto pchnąć produkt — nie zawsze jako pomysłodawca, ale jako osoba, która sprawdzała temat do końca. Nieformalny mentoring: ludzie przychodzili, żebym wytłumaczył im architekturę i produkt — nie na pięć minut, tylko na sesje po kilkadziesiąt minut; do tego demo ostatnich zmian dla zespołu i dokumentacja tam, gdzie była tego warta.

02Projekty własne

2026 — obecnie

Zobacz demo ↗

Fullstack Developer / AI Engineer

StoryForge — własny produkt SaaS · projekt po godzinach

Pomysłodawca i jedyny autor platformy SaaS dla autorów książek: edytor z historią wersji, czat RAG po treści własnej książki, analiza stylu i system kredytów.

  • Zbudowałem edytor z historią wersji i autozapisem, rozdzielając zapis roboczy, punkt w historii i indeks, z którego korzysta AI — interfejs nie udaje, że model zna nowszy tekst, niż zna naprawdę. zobacz case study ↓
  • Zaprojektowałem i wdrożyłem agenta importu książek z PDF (FastAPI + LangGraph + Gemini, za proxy w Strapi): rozdziały wykrywa kod — spis treści PDF albo skan nagłówków z kontrolą ciągłości numeracji — a model dostaje głos tylko przy kilku konwencjach i wybiera z zamkniętej listy; gdy podziału nie da się ustalić, import przerywa się zamiast wymyślać rozdziały. Zamiast paska postępu autor czyta narrację agenta (25 kodów zdarzeń w PL/EN), a nieudany import zwraca kredyty przez transakcyjne sesje w Strapi. 569 testów offline z twardą blokadą żywego modelu.
  • Czat i analiza fragmentu na treści własnej książki (Next.js, FastAPI, Qdrant): sześć trybów analizy, z których cztery świadomie nie odpytują bazy wektorowej, wyszukiwanie wektorem fragmentu zamiast pytania, twarde filtry i komunikat wprost przy braku trafień. zobacz case study ↓
  • Analizę stylu napisałem bez modelu — własne heurystyki granic zdań, dialogów i powtórzeń w oknie, z wersjonowaniem metryk. Zero kosztu inferencji i ten sam wynik przy każdym uruchomieniu.
  • Zadania AI idą przez kolejkę w Postgresie, a kredyty przez transakcyjny ledger z refundacją, gdy strumień modelu padnie przed pierwszym tokenem — awaria po stronie AI nie blokuje zapisu tekstu.
  • Całą infrastrukturę prowadzę sam: serwisy za Traefikiem, Docker, pipeline'y GitLab CI/CD i zaskryptowane stawianie środowiska od czystego serwera, ze smoke testami E2E bez płatnych wywołań AI.

Frontend Developer

Folstar — strona firmowa mikroperforacja.pl · zlecenie freelance, bezpośrednio dla klienta

Strona firmowa rodzinnego producenta opakowań foliowych i hurtowni słodyczy spod Krakowa, przebudowana od nowa na Next.js 16 i React 19. Zlecenie prowadzone bezpośrednio z klientem, bez pośredników: zakres, treść i SEO, wdrożenie i utrzymanie — wszystko, czego mała firma potrzebuje od jednej osoby.

  • Rozwiązania dobrane do skali klienta: zamiast headless CMS-a z poprzedniej wersji treść w kodzie — przy kilkunastu produktach to mniej usług do utrzymania i zero zależności od zewnętrznego dostawcy, a każda zmiana treści przechodzi tę samą ścieżkę co kod.
  • Reszta to zwykły warsztat strony firmowej, zrobiony porządnie: View Transitions w React 19 z poszanowaniem ograniczenia animacji w systemie, dostępny dialog produktu, Analytics i mapa Google dopiero po zgodzie, SEO lokalne (LocalBusiness w JSON-LD), obraz Dockera za Traefikiem wdrażany z GitLab CI na własny VPS.

03Umiejętności

Frontend

  • TypeScript
  • Vue / Nuxt
  • React / Next.js
  • View Transitions (React 19)
  • Tailwind CSS
  • Pinia
  • TanStack Query
  • VueUse
  • Zustand
  • shadcn/ui + Radix
  • i18n (pluralizacja, lokalizowane ścieżki)
  • Zod (walidacja danych z backendu)
  • Figma (wdrożenia pixel-perfect, odczyt makiet przez MCP)

Backend

  • Node.js
  • Strapi
  • Python / FastAPI
  • SQLAlchemy
  • PostgreSQL
  • Redis

AI & Data

  • RAG
  • Bazy wektorowe (Qdrant)
  • Embeddingi
  • Ewaluacja (LLM-as-judge)
  • Gemini API
  • Vercel AI SDK (streaming)
  • LangChain / LangGraph
  • LangSmith
  • Pydantic AI
  • Narzędzia dla modelu (function calling)

Architektura i DevOps

  • Vertical Slice
  • BFF
  • Docker
  • Traefik
  • GitLab CI/CD
  • VPS

Jakość i zgodność

  • Vitest
  • pytest
  • Playwright
  • Core Web Vitals
  • Sentry
  • RODO / anonimizacja danych
  • GA4 + consent mode

Procesy

  • AI-Driven Development (Claude Code: TDD, subagenci, każdą zmianę czytam i poprawiam sam)
  • Agile / Scrum
  • Code Review

Języki

  • polski — ojczysty
  • angielski — B2

04Wybrane case studies

Krótkie historie inżynierskie — problem, decyzja i policzony efekt.

Platforma rekrutacyjna (NDA)

Benchmark rozstrzygnął, która wersja wyszukiwarki jest lepsza

Najpierw pomiar: niezależny sędzia AI, który nie widzi punktacji systemu, i benchmark zbudowany z logów realnych wyszukiwań, z oczekiwaniami spisanymi ręcznie. Pierwsza runda pokazała, że problemem są dane, nie model — naprawa zapisów umiejętności podniosła zgodność rankingu z niezależną oceną z 2% do 52%, a R&D rerankingu cross-encoderem skończyło się udokumentowanym „nie wdrażamy” — z wnioskiem, że następny skok da dopiero reranking LLM. Potem poszedłem dalej: druga wersja silnika zbudowana w izolacji od produkcji, sędzia skalibrowany jak przyrząd pomiarowy (powtarzalność, praktyczny sufit ≈ 80%), wagi rankingu wyprowadzone z korelacji liczonych per ogłoszenie, lekki reranker LLM porządkujący shortlistę bez prawa odrzucania — realizacja tamtego wniosku — i ponad 440 testów offline na atrapie modelu zwracającej surowy tekst.

Na 50 ogłoszeniach benchmarku nowa wersja układa listę bliżej raportu AI w 22 przypadkach, wersja wyjściowa w 2; zgodność kolejności 70% wobec 35%, trafiony pierwszy wynik 47% wobec 36%. Nowa wersja obsługuje ruch produkcyjny — bilans po wdrożeniu, liczony na 1214 wierszach logów, wykazał cztery klasy defektów zdjęte z listy klienta, a średnia korelacja Spearmana z sędzią na realnych wyszukiwaniach trzyma się 0,72 (108 wyszukiwań, stan z września 2026).

Przeczytaj pełne case study

Platforma rekrutacyjna (NDA)

Wektory nie odróżniały zawodów

Zapytanie o księgowego zwracało kontrolerów finansowych i analityków — kompetencje mają podobne, a samo stanowisko nie przebijało się w wektorze na tyle, żeby odciąć sąsiednie role. Efekt był odwrotny do oczekiwanego: część rzeczywistych księgowych wypadała z pierwszej dziesiątki na rzecz osób z pokrewnych zawodów. Zbudowałem kanoniczną kategorię zawodu wyznaczaną przy ekstrakcji CV i wpiąłem ją do wyszukiwania obok podobieństwa wektorowego. Zamiast filtra zero-jedynkowego jest to mapa częściowego zaufania między zawodami: kontroler finansowy wciąż może trafić na zapytanie o księgowego, ale z niższą wagą, a próg odcina to, co za daleko. Sam próg zaczął działać dopiero po ponownym przetworzeniu całej bazy — dopóki część CV nie miała kategorii, furtka „nieznane przechodzi” maskowała problem.

Rola przestała być zgadywana z podobieństwa tekstu: kategoria odpowiada za to, kim ktoś jest, a wektory za to, czego kategoria nie opisuje — doświadczenie, branżę i szczegóły umiejętności.

Narzędzie wewnętrzne klienta z branży HR (NDA)

Żaden fakt od modelu nie trafia do człowieka bez potwierdzenia

Pierwsza wersja była agentem: model sterował przebiegiem w siedmiu punktach, a bramka LLM przed wysyłką w ślepym teście na 17 kontaktach wskazała wszystkie wadliwe i nie odrzuciła żadnego dobrego. Druga wersja, napisana od nowa, przenosi ciężar z decyzji modelu na weryfikowalne fakty. System buduje własną bazę firm z ogłoszeń o pracę i profili pracodawców, a model z dostępem do wyszukiwarki dopisuje wielkość firmy, telefon i stronę — w jednym typowanym wywołaniu, z zakazem zgadywania i temperaturą zero. Odpowiedź, która nie przejdzie walidacji schematu, nie trafia do bazy; fakt z niską pewnością w ogóle nie powstaje. Każdy fakt ma źródło i status: niezweryfikowany, potwierdzony albo odrzucony. Potwierdzenie musi przyjść z niezależnego źródła, a kolejność ogniw wyznaczył koszt: najpierw strona firmy i rejestr GUS, bo są darmowe, dopiero potem płatne Google Places — i to tylko wtedy, gdy dwa darmowe zapytania wskażą tę samą firmę. Bezpiecznik wydatków siedzi w bazie i przeżywa restart, dławienie limitów rejestru jest w kliencie, a każde wywołanie modelu ma zapisany koszt. Gdy audyt pokazał, że strony firm podrzucają śmieciowe numery, potwierdzane zostały tylko pełne numery, a 51 wcześniejszych trafiło do odrzuconych.

W bazie ok. 1,3 tys. firm: 83% firm w targecie ma telefon potwierdzony niezależnym źródłem (684 z 825), a 459 firm odpadło jako poza targetem przed etapem weryfikacji. Ponad 300 testów offline z blokadą sieci i atrapą modelu.

StoryForge

Nie każde pytanie potrzebuje bazy wektorowej

Analiza zaznaczonego fragmentu ma sześć trybów, od rytmu zdań po spójność świata przedstawionego, i tryb decyduje o dwóch rzeczach naraz: co model ma zrobić oraz czy w ogóle sięgamy do bazy wektorowej. Cztery z sześciu nie sięgają do niej wcale. Tam, gdzie wyszukiwanie ma sens, szukam wektorem samego fragmentu zamiast pytania: tekst podobny do akapitu to miejsca z tymi samymi postaciami i faktami, czyli dokładnie tam, gdzie chowa się sprzeczność. Pytanie dokładam do wektora tylko w trybie dowolnym, w którym nie wiadomo z góry, czego autor szuka. Kawałek zawierający sam analizowany fragment odrzucam z wyników, żeby model nie porównywał tekstu z nim samym. Fragment razem z oknem kontekstu idzie do instrukcji systemowej, nie do wątku rozmowy.

Zakres wiedzy, z której korzysta model, wynika z wybranego trybu, a nie z przypadku. A gdy tryb potrzebuje reszty książki i nic się nie znajdzie, model mówi to wprost, zamiast oceniać spójność na podstawie samego fragmentu.

StoryForge

Autozapis, zapis i indeks AI to trzy różne stany

Autor pisze, autozapis leci w tle co dwie sekundy, ale model odpowiada z indeksu, który dogania tekst dopiero po świadomym zapisie. Rozdzieliłem te trzy stany zamiast udawać, że wszystko jest zsynchronizowane: informacja „AI zna aktualną wersję” pojawia się tylko wtedy, gdy potwierdzi to serwer i jednocześnie nie ma niezapisanych zmian na ekranie. Statystyki stylu liczy backend z treści w bazie, więc przed otwarciem panelu dopycham to, czego autozapis nie zdążył wysłać — a jeśli to się nie uda, panel się nie otwiera.

Autor zawsze wie, na czym pracuje: analiza stylu albo pokazuje liczby z aktualnego tekstu, albo nie otwiera się wcale, a czat mówi wprost, kiedy odpowiada z nieaktualnego indeksu.

Printbox

Edytor React w sklepie Nuxt

Flagowy edytor foto-produktów miał własne ścieżki zakupowe i własny router (React), a sklepy działały w Nuxt z językami konfigurowanymi w runtime. Osadzenie wymagało pogodzenia dwóch routerów: obsługi przycisków wstecz i dalej w przeglądarce, poprawnych remountów i zera zahardkodowanych ścieżek. Drugi problem wynikał z pierwszego — ścieżki zakupowe edytora renderują się po stronie klienta, więc roboty wyszukiwarek dostawały pustą stronę. Zbudowałem dla nich prerender na Cloudflare Workers, serwujący gotowy HTML z cache.

Flagowy produkt firmy sprzedaje się w każdym sklepie platformy jak jego natywna część, a jego ścieżki zakupowe są widoczne dla wyszukiwarek — prerender zastąpił przy okazji płatną usługę zewnętrzną.

Printbox

Migracja z Nuxt 2 na Nuxt 3 na żywym produkcie

Platforma przechodziła na nowy framework bez zatrzymywania sprzedaży. Odpowiadałem za przeniesienie wspólnego dema, na którym stoją wszystkie sklepy, i za nowy system budowania — dzięki wspólnemu rdzeniowi większość sklepów przeszła razem z demem, a osobnej pracy wymagali tylko klienci z własnymi zmianami w kodzie. Przy okazji przeniosłem konfigurację do Redisa i zacząłem ją czytać w trakcie działania: wcześniej część ustawień zapadała w czasie budowania, więc zmiana w panelu administracyjnym wymuszała przebudowę aplikacji, a przy wielu instancjach dane potrafiły się między nimi rozjechać.

Klientów z pełną customizacją przenieśliśmy świadomie na koniec, więc najtrudniejsze przypadki wchodziły już sprawdzoną ścieżką. Nie pamiętam ani jednego wdrożenia, które trzeba było cofnąć — nowy sposób wdrażania dawał taką możliwość w kilka minut, ale nie musieliśmy z niej korzystać.

Printbox

Wdrożenia kluczowych klientów platformy

Najważniejszy klient platformy, amerykańska sieć retail, przechodził na nowy stack razem z pełnym redesignem. Po stronie frontendu prowadziłem to wdrożenie: przeniesienie sklepu na nową bazę kodu, logowanie przez system klienta (SSO) i wpięcie nowej bramki płatności w krytyczną ścieżkę zakupową — obie integracje we współpracy z backendem, na sklepie, który przez cały czas sprzedawał. Przy pilotażu globalnego producenta elektroniki, sprawdzającego wejście na rynek produktów foto, przygotowałem większość frontendu: pełny redesign pod identyfikację wizualną klienta i integracje po stronie frontu.

Sieć retail przeszła na nową platformę, a pilotaż wyglądał na zbudowany od zera pod swoją markę — choć oba sklepy stały na tej samej bazie kodu co pozostałe wdrożenia platformy.

05Jak pracuję

Praca w zespole

W zespole e-commerce pracowaliśmy w pełnym rytmie: daily, planowania, demo, retro. Zmiany szły przez ticket, branch i merge request, więc każdą oglądał ktoś jeszcze przed scaleniem. Wspólny kod z innymi frontendowcami, konflikty do rozwiązania, na co dzień ustalenia z backendem, testerami i customer success.

Odpowiedzialność za zadanie

Powierzone zadanie biorę w całości: od zrozumienia problemu po działające wdrożenie. Zwykle wystarczy mi cel — kolejność kroków i rozwiązania po drodze ustalam sam. Wcześniej dogrywam szczegóły i czasem proponuję inne podejście, które mniejszym kosztem daje ten sam efekt.

Kod pisany z AI

Piszę z Claude Code, a każda funkcja przechodzi u mnie ten sam cykl: analiza i plan, implementacja w TDD z subagentami do zadań cząstkowych, testy i benchmarki, wdrożenie przez CI/CD. Kod, który z tego wychodzi, czytam i poprawiam jak cudzy na review, zanim trafi do repozytorium. Przy takim tempie powstaje więcej kodu niż kiedyś, więc tym bardziej zapisuję w commitach i notatkach, dlaczego coś wygląda tak, a nie inaczej.

Testy tam, gdzie błąd jest cichy

Najgorsze błędy nie wywalają aplikacji, tylko przesuwają liczby albo zmieniają kolejność wyników i wyglądają wiarygodnie. Właśnie takie miejsca staram się pokrywać testami najgęściej. Reszta też jest sprawdzana, proporcjonalnie do tego, co może pójść nie tak.

Design system i Figma

Zaczynam od tokenów kolorów, odstępów i zaokrągleń trzymanych w jednym miejscu, a powtarzające się układy zbieram do wspólnych komponentów. Widoki wdrażam pixel-perfect z Figmy, a pierwszy szkic komponentu ściągam z projektu przez serwer MCP Figmy, zamiast przepisywać wartości ręcznie.

Wydajność to nie osobny etap

Core Web Vitals, rozmiar paczki i zachowanie na słabym łączu sprawdzam przy okazji funkcji, którą akurat robię. Po stronie danych to zwykle te same rzeczy: zbić kilka zapytań do jednego, scachować to, co nie zmienia się co minutę, i oddać na front tylko te pola, które widok naprawdę wyświetla.

06Szkolenia i edukacja

Kontakt

Kraków albo zdalnie. Pracuję po polsku i po angielsku. Napisz, jeśli chcesz pogadać o współpracy albo dopytać o którykolwiek z projektów.

Używam Google Analytics, żeby wiedzieć, jak korzystasz z tej strony i co poprawić. Skrypty analityczne załadują się dopiero po Twojej zgodzie. Polityka prywatności

Napisz do mnie

Zostaw wiadomość — zwykle odpowiadam w ciągu jednego dnia roboczego.