Na początku lutego siedziałem przed kolejnym pustym repozytorium — drugi produkt, ten od ewidencji projektów grantowych, którego zaczynałem od zera. Zanim padła pierwsza linia kodu domenowego, jak zawsze ustawiałem fundamenty. Czyli kopiowałem instrukcje dla AI z trzech już istniejących projektów, otwartych w sąsiednich oknach edytora.
I po raz pierwszy zauważyłem, że każda z tych trzech wersji jest inna. Niewiele. Ten sam szkielet, jeden trochę nowszy, drugi z poprawką po nieudanym eksperymencie, trzeci zawierający regułę, której w pozostałych zabrakło, bo zapomniałem ją dopisać. Trzy wersje tej samej rzeczy, rozsmarowane po kilkunastu repozytoriach, każda gorzej pamiętana niż następna.
Wtedy dotarło do mnie, że to, co właśnie kopiuję, to nie jest „konfiguracja nowego projektu”. To jest wiedza — destylat tego, czego się nauczyłem o pracy z AI przez pół roku robienia produktu. I że ja tę wiedzę trzymam jak zwitek karteczek po szafkach: w każdym repo trochę, nigdzie w komplecie.
Pracuję z AI jak z zespołem, a nie z autouzupełnianiem
Żeby było jasne, o jakich „zasadach” mówię — bo to nie są ustawienia edytora.
W moim trybie pracy AI to nie jeden asystent, który dopisuje końcówki linii. To zestaw ról, każda ze swoją robotą. Ktoś planuje większe zadanie, zanim zaczniemy je dotykać. Ktoś inny pisze kod. Trzeci patrzy mu na ręce — sprawdza, czy nie złamaliśmy reguł czystej architektury, czy testy faktycznie testują to, co mają. Czwarty robi audyt bezpieczeństwa, zanim cokolwiek zostanie scommitowane. Piąty pamięta, dokąd zmierza projekt, i zatrzymuje nas, gdy zaczynamy zbaczać.
Każda z tych ról potrzebuje wiedzieć, czym dokładnie jest ten projekt: jakim językiem się mówi, jak tu się pisze kod, czego się nigdy nie robi. Główny produkt to NestJS i domain-driven design — inny zestaw zasad. Aplikacja mobilna to Flutter z czystą architekturą — jeszcze inny. Biblioteka DDD ma swoje reguły dotyczące tego, co wolno wystawić w publicznym API. Ten blog ma własne reguły redakcyjne (nie obnażać internals aplikacji, „juz-ide” (nie stara nazwa marki), filary i tagi z zamkniętej listy).
Każda z tych ról × każdy z tych projektów = sporo instrukcji do utrzymania. A ja przez osiem miesięcy pisałem je w każdym repo od zera.
Trzynaście wzorców i jeden moment zatrzymania
Kiedy w końcu usiadłem do roboty na poważnie, wyciągnąłem trzynaście sprawdzonych wzorców do osobnego repozytorium. Trzynaście — bo tyle przeżyło przegląd po pół roku użycia. Coś, co kiedyś wydawało się dobrą praktyką, a w międzyczasie okazało się cargo cultem, nie weszło. Coś, co weszło rok temu „na zapas”, a nigdy nie zostało użyte, też nie.
Nie wymienię całej listy, bo to nie jest pointą — pointą jest kategoria. Tam są reguły dotyczące tego, jak organizuje się pliki w projekcie. Sposób opisywania ról dla agentów — kim jest planista, kim recenzent kodu, kim specjalista od bezpieczeństwa. Rytm pracy z gitem, konwencje commitów. Wymagania testowe (co najmniej 80% pokrycia, tryb test-driven). Checklisty bezpieczeństwa, których nie wolno przeskoczyć przed commitem. Konfiguracja zaczepów (hooków), które uruchamiają się automatycznie przy edycji plików — różna dla różnych stosów technologicznych.
Przy okazji zrobiłem porządek tam, gdzie się rozrosło bez powodu. Zestaw poleceń, który dorobił się siedemnastu pozycji — wszystkie kiedyś brzmiały jak dobry pomysł — ścisnąłem do trzech, których naprawdę używam codziennie. Reszta jechała w archiwum z dopiskiem „może wrócę”. Wrócę albo nie wrócę, ale przynajmniej nie zaśmieca głównego menu.
To samo, co z biblioteką DDD, tylko piętro wyżej
Pisałem już kiedyś, że własna biblioteka DDD wzięła się stąd, że zauważyłem, jak piszę kod domenowy — i że ten kształt powtarza się projekt za projektem. Wtedy wydzieliłem sposób, w jaki piszę kod. Teraz wydzieliłem sposób, w jaki pracuję z AI.
To jest dokładnie ten sam ruch. Zauważasz, że odtwarzasz pewien kształt ręcznie, raz za razem. Zauważasz, że za każdym razem trochę gorzej. Wyciągasz go raz, porządnie, w jedno miejsce — i od tej pory każdy nowy projekt dostaje go gotowego, nie buduje od zera.
Trzy lata pracy w przedsiębiorstwach nauczyło mnie, że to nazywa się „shared services” albo „platform team”. Tylko że tutaj nie ma żadnego zespołu, nie ma platformy, nie ma services. Jest jeden człowiek i kilka repozytoriów, i to wystarczy, żeby pojawiła się ta sama potrzeba: jedno źródło prawdy zamiast trzynastu kopii.
Sufit jednego człowieka
Z tego wszystkiego najważniejsze jest dla mnie to: jeden człowiek ma ograniczoną pojemność. To jest twardy fakt, nie metafora. Pracuję najlepiej przez sześć–siedem godzin dziennie. Mogę pchać dziesięć, ale jakość trzech ostatnich godzin nie zwraca poniesionych kosztów.
Mogę tę pojemność skalować na dwa sposoby. Pierwszy: pracować więcej godzin — ten sposób ma twardy sufit i ja go już znalazłem. Drugi: sprawić, żeby to, czego się raz nauczyłem, działało samo w każdym kolejnym projekcie, bez mojego udziału. Ten sposób nie ma sufitu. Każdy wzorzec, który wyciągniesz raz porządnie i zostawisz w jednym miejscu, oszczędza ci pół godziny w pięciu, dziesięciu, dwudziestu nowych projektach. Suma tych pół godzin to są tygodnie twojego życia, które dostajesz z powrotem.
Dla solo developera to nie jest „nice to have”. To jest jedyna droga, żeby nadążyć za sobą samym, gdy chce się prowadzić więcej niż jedno repo jednocześnie — a tak właśnie pracuję.
Co z tego ma czytelnik, dla którego to robię
To wszystko jest zaplecze, którego nigdy nie zobaczy nikt poza mną. Konfiguracja, reguły, hooki, role agentów — żaden z przyszłych użytkowników juz-ide nie ma o tym pojęcia ani powodu, żeby miał. Ale to ono decyduje, czy jedna osoba zdoła dowieźć tę część, która naprawdę ma znaczenie — miejsce, gdzie sąsiad za ścianą znowu ma powód, żeby się odezwać do tego z naprzeciwka. Im mniej czasu zżerają mi powtarzalne fundamenty, tym więcej zostaje na to.