← Teksty

Jak uczę się programować z AI (technicznie)

Zacznijmy od kontekst i ograniczeń, które sam sobie narzuciłem. Cała ścieżka to pięć faz o rosnącej złożoności technicznej, ułożonych tak, by każdy projekt wprowadzał dokładnie jeden nowy wymiar trudności:

  • Faza 1 — fundamenty: czysty frontend (HTML/CSS/JS + Vite, bez frameworka), git, deploy, localStorage. Projekt: Focus Space.
  • Faza 2 — backend + baza: Next.js, Supabase (baza + auth), wysyłka maili, scheduled jobs.
  • Faza 3 — SaaS z AI i płatnościami: integracja z Anthropic API, frameworki copywriterskie jako logika biznesowa, Stripe.
  • Faza 4 — real-time: Supabase Realtime / WebSockets, synchronizacja stanu między wieloma użytkownikami, obsługa konfliktów.
  • Faza 5 — mobile: Swift/SwiftUI, push notifications, docelowo Screen Time API na iOS.

Świadome ograniczenie w Fazie 1: zero backendu, zero frameworka. Vanilla JS na Vite. Chodzi o to, żeby zrozumieć fundament, zanim wejdą abstrakcje. Focus Space nie ma żadnej bazy — cały stan trwały to dwa pola w localStorage (domyślny czas misji i przerwy). Sama „misja" żyje wyłącznie w pamięci i tworzy się od nowa przy każdym starcie.

Focus Space — co jest technicznie ciekawe

Na papierze to timer Pomodoro. W praktyce kilka rzeczy wymagało realnych decyzji:

Wykrywanie rozproszenia przez Page Visibility API. Tryb „Do Not Disturb" nasłuchuje visibilitychange. Utrata widoczności zakładki na dłużej niż 10 sekund (grace period, żeby nie karać za przypadkowe mrugnięcie) kończy misję porażką. To jedyny „systemowy" hook w całej Fazie 1 i świadomie prosty — bo pełny, system-level blocker to dopiero Faza 5 na iOS (FamilyControls / ManagedSettings / DeviceActivity). Wersja webowa jest celowo słabsza, ale uczy mechanizmu.

Stan jako maszyna stanów. Misja przechodzi przez zdefiniowany zbiór statusów: config → focus → focus-overtime → completed → break → break-overtime → failed. Kluczowa decyzja projektowa: „przerwa" i „cel misji" nie są osobnymi encjami — to pola encji Misja, bo nie mają bez niej sensu. Timer nie zatrzymuje się na zerze; wchodzi w tryb overtime i liczy w górę, z osobnym wskaźnikiem wizualnym. Bez twardego limitu w v1 (świadoma decyzja, odnotowana jako ryzyko).

Pasek chłodzenia na przerwie. Podczas przerwy pasek „schładza napęd" w tempie dokładnie odpowiadającym długości przerwy — 5 min przerwy = pełne schłodzenie po 5 min. Ręczna regulacja ±1 min dopełnia pasek proporcjonalnie i dotyczy tylko bieżącej przerwy, bez zapisu do ustawień. Po zejściu do zera zegar wchodzi w overtime, a pasek zostaje wyzerowany.

Nic z tego nie jest rakietową technologią. Ale każdy z tych punktów to miejsce, gdzie „napisz timer Pomodoro" rozjeżdża się na kilkanaście konkretnych decyzji — i to właśnie te decyzje są nauką.

Workflow — najważniejsza rzecz z całego tekstu

Pracuję dwoma narzędziami AI: Claude Design do makiet UI i Claude Code do implementacji. Między nimi jest dokument PRD (Product Requirements Document), który sam piszę przed każdym projektem.

Kolejność jest nienegocjowalna: PRD → projekt ekranów → dopiero kod. Rozbijam implementację na ponumerowane kroki, każdy z własnym kryterium „jak sprawdzić, że działa", i nie przechodzę dalej, dopóki poprzedni krok tego kryterium nie spełnia. Prompty do Claude Code są pisane dyrektywnie: zakres ograniczony do bieżącego kroku, jawnie wypisane granice „out of scope", instrukcja „opisz planowane zmiany, zanim napiszesz kod" i „zatrzymaj się i zapytaj zamiast zgadywać".

Pułapka, która okazała się najciekawszą lekcją

Największe odkrycie tego projektu nie jest o kodzie, tylko o synchronizacji między projektem a implementacją.

Zauważyłem, że jeśli nie wkleję makiety z Claude Design wprost do prompta, Claude Code wymyśli własny design — i zrobi to cicho, bez ostrzeżenia. Kod będzie działał, ale jego wygląd nie będzie pochodził z żadnego zatwierdzonego projektu. Rozjazd, którego nie widać, dopóki się na niego nie natknie.

Dokładnie to stało się z jednym ekranem (ekran porażki, „Mission Failed"): powstał bez wklejonej makiety, więc działa, ale jego wygląd Claude Code wymyślił sam.

Wniosek, który wbudowałem w swój system PRD: wklejenie makiety do prompta to osobny, świadomy ruch — nie coś, co „wynika z kontekstu". I dalej: istnieją dwa niezależne tryby porażki na dwóch różnych osiach:

  • implementacja bez projektu (ekran zakodowany, ale nie z makiety),
  • projekt bez implementacji (makieta gotowa, ale jeszcze nie w kodzie).

Jedna kolumna statusu nie wyłapie obu. Dlatego w inwentarzu ekranów mam dwie osobne kolumny: „Design gotowy?" i „Zaimplementowany?". Reguła: jeśli „Zaimplementowany?" wyprzedza „Design gotowy?" — to sygnał cichego rozjazdu, który trzeba oflagować. To drobiazg w tabelce, ale dla mnie najważniejszy wynik całej Fazy 1: narzędzia AI nie utrzymają spójności za Ciebie — musisz zaprojektować proces, który to wymusza.

Gdzie teraz jestem

Focus Space jest w połowie Fazy 1. Kroki 1-6 gotowe (konfiguracja, animacja hyperspace, timer z overtime, ekran sukcesu i przerwy, tryb DND). Aktualnie: deploy na Vercel + PWA w wersji „lite" (manifest, ikony, display: standalone, bez service workera — pełne offline to osobny, późniejszy krok), żeby testować DND na prawdziwym telefonie. Potem ekran ustawień, responsywność landscape/desktop i domknięcie „definition of done".

Będę dokumentował kolejne fazy. Jeśli interesuje Cię nauka programowania sterowana AI — nie w wersji „wpisz prompt, dostań apkę", tylko z realną dyscypliną procesu wokół tego — to zostań na kolejne wpisy.

Spodobało się?
Dostań kolejny list na maila.