Back to all posts

1.3 milionów linii, 630 PR-ów tygodniowo: jak utrzymujemy bazę kodu napisaną przez AI w dobrej kondycji

1.3 milionów linii, 630 PR-ów tygodniowo: jak utrzymujemy bazę kodu napisaną przez AI w dobrej kondycji

W lipcu 2026 Robert C. Martin napisał, że nie czyta już kodu produkowanego przez swoje agenty kodujące.

To stwierdzenie przyciągnęło sporo uwagi. Jednak reszta jego wpisu jest ważniejsza dla zrozumienia, jak pracuje. Martin otacza agentów testami jednostkowymi, testami akceptacyjnymi Gherkina, procedurami QA, testami mutacyjnymi, sprawdzaniem pokrycia i metrykami jakości. Kod zyskuje jego zaufanie, przechodząc przez ten system.

Kilka miesięcy wcześniej wyraził podobną myśl. Zamiast sprawdzać implementację, patrzy na pokrycie testami, strukturę zależności, złożoność cyklomatyczną, rozmiar modułów i wyniki testów mutacyjnych. Gdy ludzie zinterpretowali to jako całkowitą rezygnację z przeglądu, doprecyzował: „Przeglądam dużo — tylko nie kod.”

Jego uwagi opisują praktyczny problem w rozwoju oprogramowania z dużym udziałem AI. Agenty kodujące mogą wprowadzać zmiany szybciej, niż ludzie są w stanie je przeczytać. Gdy ilość kodu generowanego przez AI przekracza możliwości przeglądu zespołu, inspekcja linia po linii nie może być jedynym źródłem zaufania.

Z tym samym problemem mierzymy się w vm0 od ośmiu miesięcy. Większość implementacji w naszym repozytorium piszą agenty — vibe coding, jak się to potocznie określa — a sześciu inżynierów odpowiada za to, co ten kod robi na produkcji.

W skrócie

vm0 to baza kodu napisana przez AI: agenty tworzą większość implementacji, a sześciu inżynierów odpowiada za jej działanie na produkcji. Po ośmiu miesiącach repozytorium zawiera 1,329,170 linii i w jednym tygodniu scalono 630 pull requestów. Pięć mechanizmów przejmuje ciężar jakości, którego przegląd linia po linii nie jest już w stanie unieść samodzielnie.

  • Zbieżne środowiska. Inżynierowie i agenty pracują w tym samym kontenerze deweloperskim, a każdy pull request może mieć własną gałąź bazy danych. Komenda, która działa dla agenta, działa też dla człowieka.
  • Wykonywalne ograniczenia. Surowy TypeScript, około 135 typowanych modułów kontraktów API, Oxlint, ESLint i reguły architektoniczne, a CI nie akceptuje żadnych ostrzeżeń. Reguła, którą można sprawdzić automatycznie, nie jest zostawiana do przeglądu.
  • Testy skupione na granicach. Trofeum testowe zamiast piramidy: testy integracyjne przez publiczne granice modułów, prawdziwy PostgreSQL i realne migracje, mocki tylko na granicach systemów zewnętrznych. Kod testowy to około 36 procent repozytorium.
  • Pełne pętle zwrotne. Agenty uruchamiają aplikację i bazę danych, wykonują migracje i obsługują prawdziwą przeglądarkę, po czym raportują wykonane komendy, przetestowaną ścieżkę i zrzuty ekranu. Mediana czasu scalania pull requestu to około 53 minut.
  • Ciągłe czyszczenie. Knip usuwa martwy kod deterministycznie, codzienne workflow czyszczą AI slop, którego nie wykryje żaden linter, a powtarzający się wzorzec trafia do reguły lint lub systemu typów.

Co oznaczają vibe coding i agentic coding

Vibe coding to podpowiadanie LLM-owi kodu, uruchamianie tego, co wygeneruje, proszenie o zmiany i nieprzywiązywanie uwagi do samego kodu. Definicja Martina Fowlera jest celowo wąska i podkreśla frazę „zapomnij, że kod w ogóle istnieje”. Pasuje do prototypów, jednorazowego oprogramowania i małych narzędzi o ograniczonych konsekwencjach.

Agentic coding, które Fowler nazywa agentic programming, to to, co robią zespoły, gdy zamierzają utrzymywać efekt pracy. Agent czyta repozytorium, edytuje pliki, uruchamia testy i pracuje samodzielnie przez dłuższy czas, a ludzie zachowują własność struktury i zachowania oraz przeglądają dowody z przebiegu: wyniki testów, sygnały jakości, podglądy i zachowanie produkcyjne.

Różnica nie polega na tym, ile kodu napisał model. Chodzi o to, gdzie kierowana jest ludzka uwaga.

Vibe codingAgentic coding
Autonomia agentaPodpowiedz, uruchom, podpowiedz ponownieCzyta repozytorium, edytuje, testuje, iteruje
Kto czyta kodNiktLudzie czytają to, co niesie ryzyko
Co jest weryfikowaneCzy wynik wygląda poprawnieTypy, kontrakty, testy, podglądy, sygnały produkcyjne
Najlepsze zastosowaniePrototypy i narzędzia jednorazoweOprogramowanie z perspektywą utrzymania
Typowa porażkaKod, którego nikt nie rozumieWeryfikacja zbyt słaba wobec skali zmian

Agenty edytujące repozytorium to jedna gałąź szerszej zmiany; osobno opisaliśmy jej stronę nietechniczną.

Często nazywamy vm0 projektem vibe-coded, bo to określenie stało się potocznym skrótem dla oprogramowania pisanego głównie przez AI. W ujęciu Fowlera jest to bliższe agentic coding. Nasi inżynierowie piszą mniej implementacji niż dawniej, ale nadal odpowiadają za architekturę, koszt utrzymania i zachowanie produkcyjne.

Wraz ze wzrostem szybkości generowania kodu coraz więcej odpowiedzialności przeniosło się do środowiska deweloperskiego, systemu typów, zestawu testów i zautomatyzowanych workflow utrzymaniowych.

Osiem miesięcy wzrostu bazy kodu pisanej przez AI

Repozytorium vm0 powstało w listopadzie 2025. W chwili naszego ostatniego cotygodniowego raportu skali inżynierskiej miało około ośmiu i pół miesiąca.

Repozytorium zawierało:

MetrykaLiczba
Niepuste logiczne linie1,329,170
Linie produkcyjne850,913
Linie testowe478,257
Pliki źródłowe5,549
Pliki testowe1,327
Pakiety44
Commity na głównej gałęzi14,005

Te liczby pochodzą z naszego cotygodniowego raportu skali inżynierskiej za tydzień od 20 do 26 lipca 2026, mierzone bezpośrednio z monorepo. Kod testowy stanowił około 36 procent mierzonej bazy kodu. To udział objętości kodu, nie pokrycia testami, ale daje wyobrażenie, ile implementacji powstało wokół weryfikacji.

W trzech ostatnich pełnych tygodniach sześciu inżynierów wykonało odpowiednio 541, 640 i 631 commitów. W tygodniu od 20 do 26 lipca GitHub odnotował 630 scalonych pull requestów. Sześciu inżynierów było autorami 556 z nich, a automatyzacja wydania pozostałych 74.

Mediana czasu od otwarcia pull requestu do jego scalenia wynosiła około 53 minut. 90. percentyl to około 7.4 godzin.

Przy takim tempie zmian ludzki przegląd pozostaje użyteczny, ale nie może unieść całego systemu jakości. Potrzebujemy niezależnych sygnałów na kilku etapach cyklu życia zmiany.

Nasze obecne podejście opiera się na pięciu powtarzających się motywach: zbieżnych środowiskach, wykonywalnych ograniczeniach, testach skupionych na granicach, pełnych pętlach zwrotnych i ciągłym czyszczeniu.

Zbieżność środowiska deweloperskiego dzięki dev containerom

Wiele trudnych do odtworzenia błędów wynika ze stanu, który nigdy nie pojawia się w pull requestach.

Deweloper może mieć nieudokumentowaną zmienną środowiskową, globalnie zainstalowane narzędzie, stary plik konfiguracyjny lub bazę danych działającą od miesięcy. Ludzie przyzwyczajają się do tych szczegółów. Agenty zwykle ich nie widzą.

Stopniowo przestaliśmy traktować maszynę hosta jako standardowe środowisko deweloperskie. Inżynierowie i agenty pracują w kontenerze deweloperskim. Obraz deweloperski zawiera narzędzia projektowe, PostgreSQL, pgvector, Chromium i narzędzia do automatyzacji przeglądarki.

CI uruchamia wersjonowane obrazy toolchainu budowane z tej samej rodziny wieloetapowych Dockerfile. Obrazy deweloperskie i CI służą różnym celom, a produkcja ma własny kształt wdrożenia. Kluczową cechą jest zbieżność: wersje narzędzi, zależności i założenia środowiskowe są jawne i wersjonowane.

To daje nam proste oczekiwanie. Komendy uruchamiane przez agenta powinny być powtarzalne przez inżyniera w tym samym kontenerze deweloperskim. Sprawdzenia przechodzące podczas developmentu powinny działać w bardzo podobnym toolchainie w CI.

Podobną zasadę stosujemy do danych. Każdy podgląd pull requestu może mieć własną gałąź bazy danych i uruchamiać prawdziwe migracje oraz kroki seedowania. Agent może tworzyć dane, zmieniać je i powtarzać testy destrukcyjne bez korzystania z istniejącej bazy dewelopera czy dziedziczenia stanu po innym pull requeście.

Zmiany środowiska przechodzą przez repozytorium. Aktualizacje narzędzi, wersje przeglądarek i rozszerzenia bazy danych są przeglądane i propagowane tak samo jak kod aplikacji.

Uczynienie standardów inżynierskich wykonywalnymi w typach i regułach lint

Spisane standardy pomagają ludziom rozumieć decyzje projektowe. Nie gwarantują jednak, że każda zmiana będzie ich przestrzegać, zwłaszcza podczas długich sesji agentów.

Stabilne reguły umieszczamy w systemie typów, linterach i CI, gdy tylko można je wyrazić niezawodnie.

vm0 używa surowych ustawień TypeScript, w tym strict i noUncheckedIndexedAccess. Niektóre pakiety włączają dodatkowe sprawdzenia nieużywanych wartości i niejawnych zwrotów.

API korzysta z warstwy kontraktów REST typowanych od schematu, zbudowanej na mechanizmach typów tRPC i schematach Zod. Drizzle łączy dostęp do bazy z typami TypeScript. W chwili tego przeglądu repozytorium zawierało około 135 modułów kontraktów.

Te wybory nie wychwytują błędnych decyzji produktowych. Ujawniają jednak dryf interfejsów na wczesnym etapie. Gdy pole lub odpowiedź się zmienia, powiązani wywołujący zwykle zawodzą podczas analizy statycznej. Powstały błąd jest zazwyczaj na tyle precyzyjny, że agent może go wykorzystać w kolejnej iteracji.

Lintowanie obejmuje drugą grupę reguł. Platforma uruchamia Oxlint, sprawdzenia świadome typów i ESLint, a także reguły architektoniczne specyficzne dla projektu. CI nie akceptuje żadnych ostrzeżeń. Ostrzeżenia, które mogą pozostać na stałe, stają się szumem tła, a ten łatwo zignorować zarówno ludziom, jak i agentom.

Gdy problem się powtarza, rozważamy, gdzie powinno znaleźć się sprawdzenie:

  1. Czy system typów może to wyrazić?
  2. Czy linter lub narzędzie strukturalne wykryje to dokładnie?
  3. Czy wymaga to oceny semantycznej w całym repozytorium?

Pierwsze dwa dają szybki, deterministyczny feedback przy każdej zmianie. Trzecią grupą zajmujemy się cyklicznymi workflow opisanymi dalej w artykule.

Zawężanie wzorców podatnych na błędy w kodzie generowanym przez AI

Niektóre cechy języka mają uzasadnione zastosowania, ale często pojawiają się w kruchym kodzie generowanym przez agenty.

try/catch może rozmywać granicę błędu. Gdy agent napotyka błąd, dodanie bloku catch i wartości domyślnej to łatwy sposób na utrzymanie działania bieżącej ścieżki kosztem utraty pierwotnego błędu. Mieszanie Promise .then() i .catch() z async/await rozprasza przepływ sterowania na kilka stylów.

Reactowe useEffect tworzy podobny problem w zarządzaniu stanem. Często służy do kopiowania stanu, synchronizacji dwóch źródeł prawdy lub kodowania zależności kolejności, której trudno się domyślić z modelu danych.

Platforma webowa domyślnie ogranicza te wzorce. Uzasadniony wyjątek może pozostać, z wyraźnym wyjaśnieniem obok.

Kod produkcyjny w głównej platformie webowej obecnie nie zawiera useEffect. Używamy ccstate i innych bezefektowych wzorców, by jawnie modelować zależności i efekty uboczne. Niewielka liczba wywołań useEffect w produkcji istnieje jeszcze w innych częściach monorepo, głównie we wspólnym UI i kodzie desktopowym, więc zakres twierdzenia ma znaczenie.

Te reguły wyrosły z powtarzających się awarii w tym repozytorium. Gdy wzorzec wielokrotnie powoduje ten sam problem utrzymaniowy, przesuwamy go z zaleceń przeglądowych w stronę wykonywalnego ograniczenia.

Testowanie na granicach modułów: trofeum testowe zamiast piramidy

vm0 nie stosuje klasycznej piramidy testów. Nasz przewodnik opisuje trofeum testowe: u podstaw analiza statyczna, dominującą warstwą testy integracyjne, a na szczycie niewielka liczba testów end-to-end dla kluczowych ścieżek użytkownika.

Testy jednostkowe są stosunkowo rzadkie. Używamy ich wybiórczo dla logiki wrażliwej na bezpieczeństwo, algorytmów i maszyn stanów. Większość zachowań biznesowych testujemy przez publiczną granicę modułu.

Testy API wywołują prawdziwą aplikację przez jej kontrakty. Dane testowe przygotowujemy przez produkcyjne endpointy, gdy to możliwe, a asercje wykonujemy przez publiczne zachowanie. Testy unikają bezpośredniego sięgania do wewnętrznych usług czy modyfikowania tabel bazy, bo to wiąże je z bieżącą implementacją.

Wewnętrzna infrastruktura pozostaje prawdziwa, gdy koszt jest rozsądny:

  • PostgreSQL i pgvector
  • migracje bazy danych
  • system plików
  • usługi wewnętrzne
  • mocki na granicach systemów zewnętrznych

Te testy integracyjne działają wolniej niż wysoce izolowane testy jednostkowe i wymagają pełniejszego środowiska. Zmniejszają też lukę między „test przeszedł” a „aplikacja potrafi wykonać tę operację na prawdziwej bazie”.

Testy graniczne pozwalają na duże refaktoryzacje wewnętrzne. Agent może reorganizować moduły, dzielić usługę czy zmieniać warstwę dostępu do danych, a publiczne zachowanie pozostaje chronione.

Uncle Bob preferuje inny miks, z dużym udziałem testów jednostkowych, Gherkina i testów mutacyjnych. Wspólną zasadą jest niezależna weryfikacja. Proces, który wygenerował kod, nie powinien być jedynym źródłem twierdzącym, że kod działa.

Dając agentom kodującym pełną pętlę zwrotną

Nasze wczesne uruchomienia agentów kodujących często kończyły się znajomym raportem: kod został zmieniony i TypeScript przeszedł; proszę uruchomić aplikację i sprawdzić stronę.

To zostawia połowę cyklu deweloperskiego po stronie inżyniera. Ktoś nadal musi przygotować bazę, uruchomić usługi, otworzyć przeglądarkę, utworzyć dane, zaobserwować błąd i opisać go agentowi.

Agenty mają dziś wystarczające środowisko, by wykonać więcej tej pracy. Potrafią uruchomić aplikację i bazę, wykonać migracje, utworzyć dane testowe, odwiedzić podgląd pull requestu i przeprowadzić prawdziwe interakcje w przeglądarce.

Weryfikacja w przeglądarce wychwytuje klasę problemów, których nie obejmują dobrze sprawdzenia typów i testy API: zepsuta nawigacja, stany ładowania, które nigdy się nie kończą, błędy uprawnień widoczne tylko w pełnym przepływie i regresje wizualne.

Na końcu przebiegu agent raportuje wykonane komendy, przetestowaną ścieżkę i to, co zaobserwował. Przy zmianach interfejsu użytkownika może dołączyć zrzuty ekranu. Inżynier może przejrzeć te dowody przed osobistym otwarciem podglądu.

Zrzut ekranu nie jest testem i nie dowodzi braku innych błędów. Obniża jednak koszt odtworzenia kontekstu agenta. Przy małej zmianie interfejsu powtarzalne kroki i końcowy zrzut są znacznie bardziej użyteczne niż komunikat „powinno być naprawione”.

Jakość pracy agenta zależy w dużej mierze od feedbacku, jaki może uzyskać bez czekania na człowieka.

Utrzymywanie krótkich gałęzi dzięki trunk-based development

Szybkie generowanie kodu może prowadzić do dużej liczby gałęzi.

Długo żyjące gałęzie funkcjonalne gromadzą konflikty, powieloną pracę i nieaktualny kontekst. Wraz ze wzrostem pull requestów przegląd staje się trudniejszy i wolniejszy. Stosujemy trunk-based development, by utrzymywać krótkie gałęzie i ciągłą integrację wokół main.

Zasady głównej gałęzi wymagają:

  • pull requestów do zmian
  • liniowej historii i squash merge
  • kolejki scalania
  • sprawdzeń Turbo, Rust i bezpieczeństwa
  • braku rutynowego omijania wymaganych bramek

Automatyzacja podąża tą samą ścieżką. Agent może utworzyć pull request, a niektóre niskiego ryzyka zadania utrzymaniowe mogą pozwalać na automatyczne scalanie, ale zmiana i tak przechodzi przez CI i kolejkę merge.

Rozmiar i czas życia pull requestów tłumaczą, jak 630 z nich mogło zostać scalonych w tydzień. Mała zmiana niesie mniej kontekstu, łatwiej ją zweryfikować i rzadziej koliduje z pracą produktową lub inną automatyczną naprawą.

Knip jako garbage collection repozytorium: usuwanie martwego kodu

Generowanie kodu naturalnie dodaje pliki i abstrakcje. Usuwanie zwykle wymaga osobnej podpowiedzi.

Po refaktoryzacji stare pliki mogą pozostać w repozytorium. Usunięcie funkcji może zostawić eksporty, zależności i punkty wejścia. Te artefakty rzadko psują testy, ale z czasem utrudniają nawigację po kodzie.

Używamy Knip do wykrywania nieużywanych plików, eksportów, zależności i punktów wejścia. TypeScript potwierdza, że kod jest poprawny; Knip pyta, czy nadal uczestniczy w systemie.

Pozostałości mają dodatkowy koszt dla agentów kodujących. Repozytorium to jedno z ich najważniejszych źródeł kontekstu. Przestarzały helper lub porzucona implementacja mogą wyglądać na zatwierdzony wzorzec dla kolejnego agenta. Utrzymanie czystości tego kontekstu to mniej efektowna połowa inżynierii kontekstu: to, co agent czyta z repozytorium, jest równie ważne jak podpowiedź, którą otrzymał.

Usuwanie martwego kodu poprawia też wejścia dla przyszłych przebiegów. Knip obsługuje deterministyczną część tej pracy na tyle szybko, by stać się regularnym sprawdzeniem jakości.

Powtarzalne workflow czyszczenia AI slop

Knip i ESLint mają jasne ograniczenia. Wiele form degradacji wymaga kontekstu projektu i oceny semantycznej.

Używamy „AI slop” jako praktycznego określenia tych pozostałości: niepotrzebne fallbacki, powielone abstrakcje, testy omijające publiczną granicę czy defensywne gałęzie dla niemożliwych stanów. Każdy przypadek może wydawać się nieszkodliwy. W sumie utrudniają zrozumienie repozytorium i dają kolejnym agentom złe przykłady.

Kilka powtarzalnych workflow vm0 skanuje te wzorce według harmonogramu.

Codzienne czyszczenie AI slop szuka nowych pozostałości i wybiera niewielki zestaw poprawek o wysokim prawdopodobieństwie i niskim ryzyku. Inne workflow sprawdzają testy API sięgające do usług wewnętrznych, antywzorce React i ccstate oraz dług techniczny możliwy do bezpiecznego usunięcia.

Każde workflow utrzymuje wąski zakres zmian. Otwiera pull request, a następnie polega na zwykłych sprawdzeniach typów, regułach lint, testach i kolejce merge. Pull request skonfigurowany do automatycznego scalania i tak musi przejść te same bramki.

Uruchamianie agentów według harmonogramu nie jest darmowe i osobno opisaliśmy jak ograniczać te koszty. Workflow nie próbują usunąć całego długu technicznego za jednym razem. Mała codzienna partia jest łatwiejsza do weryfikacji i mniej inwazyjna niż duże czyszczenie co kilka miesięcy.

Gdy powtarzalny workflow często znajduje ten sam wzorzec, rozważamy przeniesienie sprawdzenia do ESLint, Knip lub systemu typów. Workflow semantyczne służy jako miejsce obserwacji i dopracowania reguły przed zamianą jej w tańsze, deterministyczne sprawdzenie.

Zamiana niestabilnych testów na automatyczne naprawy

Inna grupa workflow zaczyna się od błędów GitHub Actions.

Gdy test zawiedzie na głównej gałęzi lub w kolejce merge, workflow czyta logi, sprawdza oznaki niestabilności i analizuje ponowne próby, czasy i czynniki środowiskowe. Jeśli dowody wskazują na konkretną naprawę, aktualizuje test lub implementację, otwiera pull request i ponownie uruchamia pełne CI.

Przejście testu przy ponownej próbie nie czyni pierwotnej awarii nieszkodliwą. Zespoły polegające na przycisku retry stopniowo tracą zaufanie do czerwonych buildów. Gdy to nastąpi, nieudane sprawdzenia stają się kolejną formą szumu tła.

Automatyczny workflow naprawczy zamienia sporadyczną awarię w śledzoną zmianę kodu. Diagnoza, poprawka i weryfikacja pozostają widoczne w pull requeście. Inżynierowie mogą przejrzeć zmiany wyższego ryzyka, a wąskie poprawki z mocnymi dowodami mogą przejść przez kolejkę merge.

Nasz system jakości ma obecnie trzy szerokie warstwy:

EtapMechanizmyTypowe zagadnienia
Podczas pisaniaTypeScript, kontrakty, Drizzle, ESLintbłędy typów, dryf interfejsów, znane wzorce kodu
Przed scaleniemKnip, testy integracyjne, prawdziwe bazy, podglądy, kolejka mergemartwy kod, zachowanie modułów, kompletne wyniki runtime
Po scaleniuzaplanowane i zdarzeniowe workflow vm0AI slop, semantyczne antywzorce, niestabilne testy, dryf architektoniczny

Warstwy zasilają się nawzajem. Problemy wykryte przez workflow mogą stać się regułami statycznymi. Awarii wykrytych w CI lub produkcji mogą prowadzić do nowych testów i wytycznych inżynierskich.

Gdzie kierowana jest ludzka uwaga przy przeglądzie kodu generowanego przez AI

Inżynierowie vm0 nadal czytają kod, szczególnie przy zmianach architektonicznych, wrażliwych na bezpieczeństwo, płatnościach i migracjach danych. Nie wprowadziliśmy zasady „nigdy nie czytaj kodu”.

Zmienił się jednak rozkład uwagi podczas przeglądu. Czytanie kodu to jeden z sygnałów obok kontraktów, granic testów, zachowania podglądu, zrzutów ekranu, metryk jakości i diagnostyki workflow.

Kilka decyzji nadal wymaga doświadczonego osądu:

  • czy wymaganie jest kompletne
  • gdzie powinny przebiegać granice modułów
  • które awarie są odzyskiwalne
  • jaki wpływ biznesowy może mieć błąd
  • czy model bezpieczeństwa jest odpowiedni
  • jaki nowy wzorzec awarii nie jest jeszcze objęty regułami

Podobna zmiana zaszła po stronie projektowej, gdzie design-as-code przeniósł decyzje wizualne do tego samego repozytorium i ścieżki przeglądu. Inżynierowie utrzymują też środowisko wokół agentów. Gdy problem się powtarza, decydujemy, czy dodać ograniczenie typów, regułę lint, test czy powtarzalny workflow. System developmentu stał się ważnym artefaktem inżynierskim samym w sobie.

Czytelność kodu nadal ma znaczenie. Kolejnym czytelnikiem może być inżynier lub inny agent. Splątany kod zużywa więcej kontekstu, poszerza zakres przyszłych zmian i utrudnia weryfikację.

Bezpieczeństwo i zmiany, które nie przyspieszają

Szybkość nie dotyczy wszystkiego. vm0 utrzymuje krótką listę zmian, które mają własne tempo: prace wrażliwe na bezpieczeństwo, płatności, migracje danych i decyzje architektoniczne. Inżynier czyta je linia po linii, niezależnie od tego, kto lub co je napisało.

Ryzyko bezpieczeństwa kodu generowanego przez AI, z naszego doświadczenia, to nie tyle egzotyczne podatności, co wiarygodny kod, którego nikt nie posiada. Kluczowe są zwykłe mechanizmy, stosowane konsekwentnie:

  • Testy jednostkowe, których używamy oszczędnie, są stosowane celowo dla logiki wrażliwej na bezpieczeństwo, algorytmów i maszyn stanów.
  • Testy mockują tylko na granicach systemów zewnętrznych. Infrastruktura wewnętrzna pozostaje prawdziwa, więc zmiana łamiąca kontrakt wewnętrzny zawiedzie w CI, a nie na produkcji.
  • Agenty pracują w kontenerze deweloperskim na osobnej gałęzi bazy dla każdego pull requestu, nie na maszynie dewelopera ani na wspólnej bazie.
  • Kolejka merge i wymagane sprawdzenia nie mają rutynowego obejścia, także dla pull requestu otwartego przez agenta i oznaczonego do automatycznego scalenia.
  • try/catch jest domyślnie ograniczony, więc awaria rzadziej zostanie przechwycona przez fallback dodany przez agenta, by utrzymać ścieżkę.

Obsługa poświadczeń to osobny problem projektowy z własnym rozwiązaniem: opisaliśmy wzorzec brokera, który trzyma tokeny poza zasięgiem agenta w osobnym artykule.

To wszystko nie czyni kodu generowanego bezpiecznym samo w sobie. Zawęża zestaw zmian, gdzie decyzja człowieka jest jedyną kontrolą, i czyni ten zestaw jawnym.

Czego te liczby nie pokazują

Cotygodniowy raport pokazuje skalę repozytorium i tempo dostarczania. Sam w sobie nie pokazuje niezawodności produkcyjnej.

Ocena wpływu na jakość runtime wymaga dostępności, wskaźników błędów produkcyjnych, liczby incydentów, wskaźnika niepowodzeń zmian, częstotliwości rollbacków i średniego czasu naprawy. Zielony przebieg CI opisuje tylko część procesu dostarczania.

Nadal zbieramy te miary efektów. Praktyki inżynierskie wyjaśniają, jak system zarządza ryzykiem; dane produkcyjne pokazują, jak skutecznie to działa.

FAQ

Czym jest vibe coding? Vibe coding to podpowiadanie LLM-owi kodu, uruchamianie tego, co zwraca, proszenie o zmiany i nieczytanie wygenerowanego kodu. Definicja Martina Fowlera jest celowo wąska: deweloper ma „zapomnieć, że kod w ogóle istnieje”. Pasuje do prototypów, jednorazowego oprogramowania i narzędzi o ograniczonych konsekwencjach.

Czym jest agentic coding? Agentic coding to dłużej trwająca forma rozwoju wspomaganego przez AI, w której agent czyta repozytorium, edytuje pliki, uruchamia testy i iteruje samodzielnie przez dłuższy czas. Ludzie nadal posiadają architekturę i zachowanie oraz przeglądają dowody z przebiegu, a nie każdą napisaną linię.

Jaka jest różnica między vibe coding a agentic coding? Uwaga, nie autorstwo. W vibe coding kod nigdy nie jest sprawdzany. W agentic coding agent pracuje samodzielnie, a inżynierowie sprawdzają dowody wokół niego: wyniki testów, sygnały jakości, podglądy i zachowanie produkcyjne. vm0 zwykle opisuje się jako vibe-coded; w ujęciu Fowlera to agentic coding.

Czym jest AI slop? AI slop to pozostałości po kodzie generowanym przez AI: niepotrzebne fallbacki, powielone abstrakcje, testy omijające publiczną granicę, defensywne gałęzie dla niemożliwych stanów. Każdy przypadek wygląda nieszkodliwie. W sumie utrudniają zrozumienie repozytorium i dają kolejnym agentom złe przykłady.

Jak przeglądacie kod generowany przez AI przy 630 pull requestach tygodniowo? Nie linia po linii. W vm0 ludzki przegląd trafia tam, gdzie potrzebny jest osąd: architektura, bezpieczeństwo, płatności, migracje danych. Resztę niosą mechanizmy: surowe typy i kontrakty, testy integracyjne na granicach modułów, prawdziwe bazy w podglądach per pull request, weryfikacja w przeglądarce, Knip i kolejka merge, której żadna zmiana rutynowo nie omija.

Jakie są najlepsze praktyki vibe codingu na dużą skalę? Pięć sprawdza się przez osiem miesięcy w vm0: zbieżne środowisko, by agenty i ludzie uruchamiali te same komendy; standardy wykonywalne w typach, linterach i CI zamiast dokumentów; testowanie na granicach modułów na prawdziwej infrastrukturze; pełna pętla zwrotna dla agentów, w tym przeglądarka; i ciągłe czyszczenie zamiast okazjonalnych dużych porządków.

Jakie są ryzyka bezpieczeństwa kodu generowanego przez AI? Typowe ryzyko to nie egzotyczna podatność, lecz wiarygodny kod, którego nikt nie posiada. vm0 utrzymuje prace wrażliwe na bezpieczeństwo, płatności i migracje danych pod ludzkim przeglądem, używa testów jednostkowych celowo dla tej logiki, utrzymuje prawdziwą infrastrukturę w testach, ogranicza wzorce jak try/catch, które przechwytują błędy, i nie pozwala rutynowo omijać wymaganych sprawdzeń.

Czy kod generowany przez AI tworzy dług techniczny? Tworzy specyficzny rodzaj: kod, który nadal się kompiluje i przechodzi testy, ale nie uczestniczy już w systemie, plus semantyczne pozostałości, których nie nazwie żaden linter. Knip usuwa część deterministyczną. Powtarzalne workflow obsługują resztę w małych codziennych partiach, a często powtarzający się wzorzec staje się regułą lint lub ograniczeniem typów.

Ile kodu vm0 jest napisane przez AI? Większość implementacji. Sześciu inżynierów napisało 556 z 630 pull requestów scalonych w tygodniu od 20 do 26 lipca 2026, a resztę napisała automatyzacja wydania, a agenty napisały większość kodu w tych pull requestach. Inżynierowie posiadają architekturę, ograniczenia i zachowanie produkcyjne.

Czym jest trofeum testowe i dlaczego nie piramida testów? Trofeum testowe stawia analizę statyczną u podstaw, testy integracyjne jako dominującą warstwę i niewielką liczbę testów end-to-end na szczycie. vm0 stosuje je, bo testy przez publiczną granicę modułu chronią zachowanie, gdy agent reorganizuje implementację pod spodem, czego nie robi duża warstwa testów jednostkowych.

Jak znaleźć martwy kod w repozytorium pisanym przez AI? Generowanie kodu dodaje pliki i abstrakcje; usuwanie zwykle wymaga osobnej podpowiedzi. vm0 uruchamia Knip jako regularne sprawdzenie, by znaleźć nieużywane pliki, eksporty, zależności i punkty wejścia. TypeScript potwierdza, że kod jest poprawny; Knip pyta, czy nadal uczestniczy w systemie, co jest kluczowe dla martwego kodu.

Czy kod vibe-coded jest bezpieczny do uruchomienia na produkcji? To zależy od tego, co go weryfikuje, a nie od tego, kto go napisał. Wymagane sygnały się nie zmieniły: typy i kontrakty, testy przez publiczne granice, prawdziwa infrastruktura i pipeline, który musi być zielony. Liczby w tym artykule opisują skalę repozytorium i tempo dostarczania. Dostępność, wskaźniki błędów i niepowodzeń zmian to miary, które odpowiadają na pytanie produkcyjne i nadal je zbieramy.

Po ośmiu miesiącach

Osiem miesięcy to za wcześnie, by ogłaszać ostateczną metodę. Modele, narzędzia agentów i repozytorium nadal się zmieniają, a nasze reguły i workflow zmieniają się razem z nimi.

Jedna zmiana jest już widoczna. Wraz ze wzrostem szybkości generowania kodu środowisko, ograniczenia, testy i system feedbacku przejęły większą część ciężaru jakości. Inżynierowie spędzają mniej czasu na pisaniu implementacji, a więcej na definiowaniu zachowań, projektowaniu granic i ulepszaniu weryfikacji.

Baza kodu vm0 będzie nadal rosnąć. Knip usuwa deterministyczne pozostałości. Reguły statyczne blokują znane wzorce awarii. Testy integracyjne chronią zachowanie modułów. Powtarzalne workflow obsługują degradację, której nie umiemy jeszcze wyrazić mechanicznie.

Kod nadal ma znaczenie. Najlepsze praktyki z tego artykułu nie polegają na pisaniu go mniej, lecz na decydowaniu, co musi być prawdą, zanim zmiana trafi na główną gałąź. Obecnie używamy więcej dowodów wykonywalnych przez maszyny, by zdecydować, czy zmiana powinna trafić na główną gałąź i czy jej implementacja powinna pozostać w repozytorium.

Stay in the loop

// Get the latest insights on AI teammates and collaboration.