Code is cheap, judgment is expensive: dlaczego w erze AI wracają fundamenty programowania
AI obniża koszt pisania kodu prawie do zera, ale nie koszt złych decyzji. Dlaczego review, architektura, testy i security znów decydują o jakości.
Code is cheap, judgment is expensive
Dlaczego w erze AI wracają fundamenty programowania i dlaczego najważniejsze pytanie w code review brzmi dziś: „czy tak w ogóle powinno być?”
W 2026 najcenniejszą umiejętnością programisty nie jest już pisanie kodu. Jest nią umiejętność powiedzenia AI: ten kod jest zły — i wyjaśnienia dlaczego.
Brzmi jak hasło na konferencyjny slajd, więc zacznijmy od czegoś mniej efektownego: od pull requesta.
Agent dostaje zadanie „dodaj rezerwację produktu w koszyku”. Po kilku minutach jest PR: kilkaset linii, nowy endpoint, migracja, kilkanaście testów, wszystkie zielone. Opis zmian jest staranniejszy niż ten, który napisałby zmęczony człowiek w piątek po południu. Review trwa pięć minut, bo wszystko „wygląda dobrze”.
Trzy tygodnie później, podczas wyprzedaży, sklep sprzedaje 40 sztuk produktu, którego było 12.
Kod działał. Testy przechodziły. Rozwiązanie było złe. Do tego PR-a jeszcze wrócimy.
Ten tekst nie twierdzi, że AI pisze zły kod. Często pisze kod lepszy niż przeciętny. Twierdzi coś innego: AI radykalnie obniża koszt wygenerowania kodu, ale podnosi wartość umiejętności oceny, czy ten kod jest poprawny, bezpieczny, utrzymywalny i właściwy architektonicznie. Najcenniejszy programista to dziś nie ten, kto najszybciej produkuje kod, tylko ten, kto potrafi ocenić, czy rozwiązanie powinno w ogóle powstać w tej formie.
W skrócie:
- Generowanie kodu tanieje szybciej niż jego weryfikacja. Wąskie gardło przesunęło się z pisania na review i osąd.
- Kod z AI rzadko jest po prostu błędny. Częściej jest poprawny lokalnie: przechodzi testy, ale ma zły model danych, wyścig, dziurę w autoryzacji albo szkodzi reszcie systemu.
- Osąd zapisany raz, w testach, ograniczeniach i uprawnieniach, skaluje się. Osąd stosowany od nowa przy każdym PR-ze — nie.
- AI działające wewnątrz aplikacji powinno mieć uprawnienia użytkownika, w którego imieniu działa, a zmianę danych powinien zatwierdzać człowiek.
- Juniorzy, którzy w pełni delegują pracę AI, uczą się wyraźnie mniej niż ci, którzy pytają AI o wyjaśnienia.
Kod staje się commodity. Decyzje nie
Simon Willison, współtwórca Django i autor Datasette, otwiera przewodnik Agentic Engineering Patterns rozdziałem „Writing code is cheap now”. Teza jest prosta: koszt wyprodukowania pierwszej działającej wersji kodu spadł prawie do zera, ale dostarczenie dobrego kodu nadal kosztuje dużo.
Ciekawsze od samej tezy jest to, jak Willison definiuje „dobry kod”. Kod ma działać, ale też: dać się zweryfikować, rozwiązywać właściwy problem, sensownie obsługiwać błędy, być możliwie prosty, mieć testy, aktualną dokumentację, dać się zmieniać w przyszłości i spełniać wymagania jakościowe, takie jak bezpieczeństwo czy niezawodność.
Z tej listy tanieje domyślnie tylko pierwszy punkt. Cała reszta to decyzje.
Kent Beck, twórca Extreme Programming, ujmuje to samo zjawisko we wpisie z kwietnia 2023 roku, napisanym po pierwszej próbie z ChatGPT: wartość 90% umiejętności Becka spadła do zera, a dźwignia pozostałych 10% wzrosła tysiąckrotnie. W eseju rozwijającym ten wpis tymi 10% okazuje się wiedza, co warto zrobić i o co zapytać, a nie sprawne układanie słów jedno po drugim.
Jest też prostsze, ekonomiczne wyjaśnienie. W eseju „Strategy Letter V” z 2002 roku Joel Spolsky przypomina podstawową zasadę: gdy tanieje dany produkt, rośnie popyt na produkty komplementarne. Kod i ocena kodu są komplementarne. Kiedy kod tanieje, powstaje go więcej, a każda nowa linia potrzebuje kogoś, kto zdecyduje, czy powinna trafić na produkcję.
Najstarsze uzasadnienie pochodzi z 1986 roku. W „No Silver Bullet” Fred Brooks rozdziela złożoność oprogramowania na przypadkową i istotną. Przypadkowa wynika z narzędzi: składni, boilerplate'u, szukania sygnatur w dokumentacji. Istotna wynika z samego problemu: co system ma robić, jakie warunki muszą być zawsze spełnione, co się dzieje, gdy coś się psuje. Według Brooksa żadne pojedyncze narzędzie nie da skoku produktywności o rząd wielkości, bo nie usunie złożoności istotnej.
LLM-y to najpoważniejszy kandydat na srebrną kulę, jaki widzieliśmy. Ale w ogromnej większości atakują złożoność przypadkową. Istotna została tam, gdzie była — po stronie człowieka.
Więcej kodu nie znaczy więcej oprogramowania
Gdyby generowanie kodu było wąskim gardłem, tańszy kod powinien przekładać się wprost na szybsze dostarczanie działającego oprogramowania. Dane pokazują coś bardziej złożonego.
| Źródło | Co zbadano | Wynik | Zastrzeżenie |
| Faros AI, 2025 | telemetria ponad 10 000 programistów z 1255 zespołów | w zespołach z wysoką adopcją AI: +98% zmergowanych PR-ów, +91% czasu review, +154% średniego rozmiaru PR, +9% bugów na programistę; brak poprawy na poziomie całej firmy | dostawca narzędzi analitycznych; korelacja, nie przyczynowość |
| DORA, 2024 | ankieta i model statystyczny | wzrost adopcji AI o 25% wiązał się ze spadkiem przepustowości dostarczania o 1,5% i stabilności o 7,2% | szacunki modelowe |
| DORA, 2025 | ankieta | AI po raz pierwszy zwiększa przepustowość, ale nadal zwiększa niestabilność; działa jak wzmacniacz tego, co w zespole już jest | dane deklaratywne |
| METR, 2025 | badanie randomizowane: 16 doświadczonych programistów, 246 zadań w dobrze im znanych repozytoriach | z AI praca trwała o 19% dłużej, choć uczestnicy oceniali, że byli o 20% szybsi | narzędzia z początku 2025; nowsze dane z 2026 sugerują przyspieszenie, ale sam METR uznaje je za niewiarygodne przez efekty selekcji |
| Stack Overflow, 2025 | ankieta wśród programistów | 66% jako główną frustrację wskazuje odpowiedzi AI „prawie dobre, ale nie do końca”; 46% nie ufa ich dokładności, 33% ufa | deklaracje, nie pomiar |
| CodeRabbit, 2025 | 470 pull requestów open source | PR-y współtworzone przez AI miały średnio 1,7× więcej problemów, a błędów logiki o 75% więcej | producent narzędzia do AI review; analiza automatyczna |
| GitClear, 2025 | 211 mln zmienionych linii kodu | udział przenoszonego kodu, czyli przybliżenie refaktoryzacji, spadł z 25% w 2021 do poniżej 10% w 2024; udział kodu kopiowanego wzrósł z 8,3% do 12,3% | zbieżność w czasie z erą asystentów AI, nie dowód przyczynowości |
Żadne z tych badań osobno nie rozstrzyga sprawy. Razem pokazują jednak spójny wzór: generowanie przyspieszyło, weryfikacja nie.
To jest prawo Amdahla zastosowane do zespołu. System działa tak szybko, jak jego najwolniejszy etap. Jeśli pisanie kodu przyspiesza kilkukrotnie, a review, testy, wdrożenie i diagnoza błędów pozostają w tym samym tempie, wąskie gardło po prostu się przesuwa. Z klawiatury na osąd.
Najbardziej niepokojący jest wynik METR, i to nie z powodu samych 19%. Problemem jest rozjazd między odczuciem a rzeczywistością. Zespół, który czuje się szybszy, nie szuka wąskiego gardła.
Działa ≠ dobrze. Sześć sposobów, w jakie AI pisze poprawny, zły kod
Poniższe przykłady mają wspólną cechę: przechodzą testy, wyglądają profesjonalnie i przeszłyby pospieszne review. To przykłady złożone z typowych wzorców, a nie opisy konkretnych incydentów. Zgadzają się jednak z tym, co OX Security opisało w raporcie „Army of Juniors” po analizie 300 repozytoriów: kod generowany przez AI jest wysoce funkcjonalny, ale systematycznie brakuje mu osądu architektonicznego. Wśród najczęstszych antywzorców raport wymienia nadmierną specyfikację, unikanie refaktoryzacji, syndrom „u mnie działało” i pozorne pokrycie testami.
1. Abstrakcja na zapas
Zadanie: „wyślij klientowi maila po opłaceniu zamówienia”.
type Notifier interface {
Notify(ctx context.Context, n Notification) error
}
type NotifierFactory struct {
registry map[Channel]func(cfg Config) (Notifier, error)
}
func (f *NotifierFactory) Register(ch Channel, ctor func(Config) (Notifier, error)) {
f.registry[ch] = ctor
}
// EmailNotifier — gotowy
// SMSNotifier, PushNotifier, WebhookNotifier — TODOKod się kompiluje, test kanału e-mail przechodzi, całość wygląda „enterprise”.
Problem w tym, że mamy cztery abstrakcje dla jednego przypadku użycia. Każdy kolejny czytelnik, człowiek albo agent, musi zrozumieć rejestr, fabrykę i konfigurację, żeby wysłać jednego maila. Puste kanały udają punkty rozszerzeń, które nigdy nie były zaprojektowane. Gdy pojawi się prawdziwy SMS, okaże się, że ma inne wymagania: zgody, koszt, limity, inne ponawianie. Do przygotowanego interfejsu i tak nie będzie pasował.
Wystarczyłaby jedna funkcja sendOrderPaidEmail(ctx, order). Abstrakcja ma sens wtedy, gdy pojawi się drugi realny przypadek, a nie wtedy, gdy model zna wzorzec projektowy.
Pytanie, które zadaje osąd: ile realnych przypadków użycia ma ta abstrakcja dzisiaj?
2. Model danych, który po miesiącu zaczyna kłamać
Zadanie: „dodaj historię zamówień”.
CREATE TABLE order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT REFERENCES orders(id),
product_id BIGINT REFERENCES products(id),
quantity INT NOT NULL
);
-- suma zamówienia: SUM(products.price * order_items.quantity)Test tworzy produkt, zamówienie i sprawdza sumę. Wszystko się zgadza.
Do pierwszej zmiany ceny. Od tego momentu historyczne zamówienia i faktury zmieniają wartość wstecz, bo pozycja zamówienia wskazuje bieżący stan produktu zamiast zapisać to, co klient naprawdę kupił. Usunięcie produktu z katalogu albo zablokuje klucz obcy, albo rozsypie historię. Jeśli do tego cena trafi do kolumny FLOAT, dochodzą błędy zaokrągleń w pieniądzach.
Poprawny model zapisuje w pozycji zamówienia snapshot: cenę jednostkową, nazwę, SKU i walutę z chwili zakupu, a kwoty przechowuje jako NUMERIC albo liczbę całkowitą w groszach. Tak zresztą działa model sklepu w GoatCMS: shop_order_item przechowuje własne title, sku, unit_price i line_total, niezależnie od późniejszych zmian produktu.
Błędy w modelu danych są najdroższe ze wszystkich, bo kod można wygenerować ponownie w minutę. Danych z dwóch lat działalności — nie.
Pytanie, które zadaje osąd: co jest faktem historycznym, a co bieżącym stanem?
3. Race condition, którego nie widzi żaden test jednostkowy
Wróćmy do PR-a z początku.
func Reserve(ctx context.Context, db *sql.DB, productID int64, qty int) error {
var stock int
err := db.QueryRowContext(ctx,
`SELECT stock FROM products WHERE id = $1`, productID).Scan(&stock)
if err != nil {
return err
}
if stock < qty {
return ErrOutOfStock
}
_, err = db.ExecContext(ctx,
`UPDATE products SET stock = $1 WHERE id = $2`, stock-qty, productID)
return err
}Testy wywołują tę funkcję po kolei, więc wszystko działa. W produkcji dwa żądania odczytują jednocześnie stock = 1, oba przechodzą warunek, oba zapisują 0. Sprzedaliśmy dwie sztuki z jednej. To klasyczny wzorzec check-then-act i zgubiona aktualizacja.
Poprawka jest krótsza niż oryginał:
UPDATE products
SET stock = stock - $1
WHERE id = $2 AND stock >= $1;
-- 0 zmienionych wierszy oznacza brak towaruTa sama klasa problemu wraca w webhookach płatności. Operator płatności ponawia powiadomienie, więc handler wykona się dwa razy. Jeśli nie ma klucza idempotencji i ograniczenia unikalności w bazie, klient dostanie dwie licencje albo dwie paczki.
Pytanie, które zadaje osąd: co się stanie, jeśli ten kod wykona się dwa razy naraz? A dwa razy po sobie?
4. Kod, który ukrywa awarie
Zadanie: „dashboard się wysypuje, gdy API płatności nie odpowiada — napraw”.
payments, err := client.ListPayments(ctx, since)
if err != nil {
log.Println("could not fetch payments")
return []Payment{}, nil
}Dashboard przestaje się wysypywać. Zgłoszenie zamknięte.
Tyle że błąd zamienił się w dane. „Zero płatności” jest teraz nieodróżnialne od „nie wiemy, ile było płatności”. Log nie zawiera przyczyny, identyfikatora żądania ani czasu odpowiedzi. Nie ma metryki ani alertu. Osoba na nocnym dyżurze widzi przychód równy zero i nie wie, czy to awaria integracji, czy problem biznesowy.
Lepsza wersja zwraca błąd albo jawny stan „dane niedostępne”, loguje strukturalnie z kontekstem, zwiększa licznik błędów integracji i dodaje span w tracingu. Observability to nie są logi „na wszelki wypadek”. To zdolność do zadania działającemu systemowi pytania, którego nikt wcześniej nie przewidział.
Pytanie, które zadaje osąd: jak dowiem się, że to nie działa, i dlaczego?
5. Podatność, której nie widać na happy path
// GET /api/orders/{id}, za middleware wymagającym zalogowania
func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {
order, err := h.repo.FindByID(r.Context(), r.PathValue("id"))
if err != nil {
http.Error(w, "not found", http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(order)
}Endpoint jest „zabezpieczony”, bo wymaga sesji. Mimo to każdy zalogowany użytkownik może odczytać cudze zamówienie, zmieniając identyfikator w URL-u. To Broken Object Level Authorization, pierwsza pozycja OWASP API Security Top 10. Testy przechodzą, bo autor sprawdza wyłącznie własne zamówienia.
Poprawka to zapytanie z właścicielem, na przykład FindByIDForOwner(ctx, id, session.UserID), i ten sam kod 404 zarówno dla rekordu nieistniejącego, jak i cudzego, żeby nie ujawniać, które identyfikatory istnieją.
Dane są tu wyjątkowo spójne. Veracode w raporcie z 2025 roku sprawdził ponad 100 modeli na 80 zadaniach: w 45% przypadków wygenerowany kod zawierał podatność, a większe i nowsze modele nie radziły sobie wyraźnie lepiej. Osobną klasą ryzyka są zależności. Badanie zaprezentowane na USENIX Security 2025 wykazało, że modele komercyjne proponowały nieistniejące paczki w co najmniej 5,2% przypadków, a otwarte w 21,7%. 43% zmyślonych nazw powtarzało się w każdym z dziesięciu powtórzeń tego samego promptu, co czyni je przewidywalnym celem: wystarczy zarejestrować paczkę o takiej nazwie. Ten atak nazywa się slopsquattingiem. Pisaliśmy o ryzyku instalowania zależności w tekście npm install nie powinno znaczyć: „uruchom obcy kod na moim komputerze”.
Pytanie, które zadaje osąd: kto jeszcze może wywołać ten kod i do czyich danych dostanie dostęp?
6. Lokalnie poprawne, globalnie szkodliwe
Zadanie: „test integracyjny czasem pada na timeout do usługi cenowej — dodaj retry”.
for attempt := 0; attempt < 4; attempt++ {
resp, err = pricing.Get(ctx, sku)
if err == nil {
break
}
}Lokalnie wszystko gra: niestabilny test robi się zielony.
Globalnie właśnie powstał wzmacniacz awarii. Google SRE Book opisuje dokładnie ten mechanizm: jeśli trzy warstwy systemu ponawiają żądanie po trzy razy, jedna akcja użytkownika może wygenerować 4 × 4 × 4 = 64 próby wobec usługi, która już ma problem. Retry bez wykładniczego backoffu, losowego jittera i budżetu ponowień zamienia spowolnienie w kaskadową awarię.
Agent widział jeden plik. Problem istnieje w grafie wywołań, którego nie było w kontekście.
Pytanie, które zadaje osąd: co ta zmiana zrobi z resztą systemu, kiedy coś już się psuje?
Test kontra osąd
| Przykład | Co sprawdził test | O co pyta osąd |
| Abstrakcja na zapas | czy mail został wysłany | ile realnych przypadków użycia ma ta abstrakcja? |
| Model danych | czy suma zamówienia się zgadza | co jest faktem historycznym, a co bieżącym stanem? |
| Współbieżność | czy rezerwacja działa | co, jeśli kod wykona się dwa razy naraz? |
| Observability | czy dashboard się nie wysypuje | jak dowiem się o awarii i jej przyczynie? |
| Bezpieczeństwo | czy właściciel widzi swoje zamówienie | kto jeszcze może to wywołać? |
| Retry | czy test przechodzi | co ta zmiana zrobi z systemem w trakcie awarii? |
Żadne z tych pytań nie dotyczy składni. Wszystkie dotyczą konsekwencji.
Senior: od „jak to napisać” do „czy tak powinno być”
Przez lata część wartości seniora wynikała z szybkości: znał API, pamiętał pułapki frameworka, pisał bez zaglądania do dokumentacji. Tę część przejmuje dziś agent, który zna więcej API niż ktokolwiek z nas.
Zostaje to, czego agent nie ma w kontekście: wiedza, czego nie budować, jak modelować dane, jak system się psuje i ile kosztuje cofnięcie decyzji.
Raport OX Security trafnie opisuje AI jako armię utalentowanych, ale niedoświadczonych juniorów. Wynika z tego niewygodny wniosek: każdy programista pracujący z agentem staje się liderem takiej armii. Juniorów, którzy nigdy się nie męczą, nie zadają pytań i zawsze brzmią pewnie.
Addy Osmani z Google nazywa tę asymetrię „problemem 70%”: AI szybko dowozi większość rozwiązania, ale ostatnie 30%, czyli przypadki brzegowe, integracje, bezpieczeństwo i wydajność, wymaga dokładnie tej wiedzy, której narzędzie nie zastępuje. Dlatego doświadczeni programiści zyskują na AI więcej. Ciągle oceniają, poprawiają i odrzucają. Mniej doświadczeni częściej akceptują wynik w całości.
Jeszcze głębiej sięga Peter Naur. Esej „Programming as Theory Building” z 1985 roku dowodzi, że program to przede wszystkim teoria w głowach ludzi, którzy go tworzą, a kod jest tylko jej częściowym zapisem. Kiedy zespół, który zna teorię, odchodzi, program zaczyna umierać, nawet jeśli kod nadal się kompiluje.
AI potrafi wygenerować kod bez teorii. Osmani nazywa skutek tego zjawiska długiem zrozumienia: rosnącą luką między ilością kodu w systemie a ilością kodu, który ktokolwiek naprawdę rozumie. Z „The Elements of Programming Style” Kernighana i Plaugera pochodzi obserwacja, że debugowanie jest dwa razy trudniejsze niż pisanie. Jeśli kod powstaje na granicy naszego rozumienia, nie będziemy w stanie go debugować.
Willison formułuje praktyczną regułę: nie commitować kodu, którego nie da się wyjaśnić komuś innemu. Jeśli kod napisany przez LLM został przejrzany, przetestowany i jest zrozumiały, to po prostu inżynieria oprogramowania. Na drugim końcu spektrum jest vibe coding, czyli budowanie bez patrzenia, jak kod działa.
Osiem pytań do każdego PR-a od AI
- Jaki problem rozwiązuje ta zmiana i czy to jest problem, który naprawdę mamy?
- Czego ta zmiana nie powinna dotykać, a dotyka?
- Jakie niezmienniki muszą być prawdziwe po zmianie i co je wymusza: test, typ, constraint w bazie czy nadzieja?
- Co się stanie przy dwóch równoległych wywołaniach albo przy ponowieniu?
- Jak to się psuje i skąd się o tym dowiemy?
- Kto może to wywołać i do jakich danych dostanie dostęp?
- Ile będzie kosztować cofnięcie tej decyzji za pół roku, gdy zależą od niej dane, API publiczne albo inne zespoły?
- Czy umiem wyjaśnić tę zmianę bez czytania kodu na głos?
Review to nowe wąskie gardło. Szybsze czytanie go nie poszerzy
Skoro osąd stał się wąskim gardłem, organizacje zaczynają go jawnie zarządzać.
W marcu 2026 roku Financial Times opisał wewnętrzne materiały Amazona, które łączyły serię awarii o „dużym promieniu rażenia” ze zmianami wspieranymi przez generatywne AI. Według FT zmiany przygotowane z pomocą AI przez osoby na niższych stanowiskach miały wymagać akceptacji seniora. Amazon zakwestionował tę relację: według firmy tylko jeden z omawianych incydentów dotyczył AI, żaden nie dotyczył kodu napisanego przez AI, a takiego wymogu nie wprowadzono. Niezależnie od tego, kto ma rację w szczegółach, sam spór pokazuje, że pytanie „kto podpisuje zmianę wygenerowaną przez AI?” stało się tematem na poziomie zarządu.
Thoughtworks Technology Radar umieszcza „complacency with AI-generated code”, czyli bezrefleksyjne zaufanie do kodu z AI, w kategorii Hold. Zespół Radaru zwraca uwagę, że agenci generują coraz większe zestawy zmian, a programiści coraz mniej chętnie je przeglądają.
Odpowiedź „przeglądajmy uważniej” nie wystarczy. Jeśli PR-ów jest dwa razy więcej, a każdy jest dwa i pół raza większy, czytanie linia po linii przestaje być strategią.
Ciekawszą odpowiedź pokazuje OpenAI w tekście o harness engineering z lutego 2026 roku. Mały zespół zbudował w pięć miesięcy produkt liczący około miliona linii kodu, nie pisząc ręcznie żadnej z nich. Ludzie projektowali środowisko pracy agentów, opisywali intencję i budowali pętle informacji zwrotnej. Granice architektury nie były opisane w dokumencie z prośbą o przestrzeganie. Wymuszały je lintery i testy strukturalne.
Birgitta Böckeler z Thoughtworks opisuje taki harness jako zestaw prowadnic, które sterują agentem przed działaniem, i czujników, które sprawdzają wynik po działaniu. Zauważa jednak, że w opisie OpenAI brakuje weryfikacji funkcjonalności i zachowania systemu. Lintery pilnują struktury. Nie powiedzą, czy system robi to, co powinien.
Z tego wynika najważniejsze rozróżnienie w tym tekście:
Osąd zastosowany raz i zapisany jako test, typ, constraint albo uprawnienie skaluje się. Osąd stosowany od nowa przy każdym PR-ze — nie.
Szerzej o zamienianiu decyzji w kontrakty pisaliśmy w tekście From Vibe Coding to Contract-Driven AI Development. Tu warto dodać jedno: AI może pomagać recenzować, ale model nie powinien być jedynym sędzią własnej pracy. Ktoś musi odpowiadać za decyzję, a odpowiedzialności nie da się przypisać modelowi.
Gdy AI staje się użytkownikiem aplikacji: osąd zapisany w uprawnieniach
Do tej pory mówiliśmy o AI, które pisze kod. Jest drugi front, coraz ważniejszy: AI, które pracuje wewnątrz aplikacji. Czyta dane, wypełnia formularze, proponuje usunięcie rekordu.
Tu pytanie nie brzmi już „czy ten kod jest dobry?”, tylko: co ten agent może zrobić, w czyim imieniu i kto to zatwierdza?
W lipcu 2025 roku agent Replit, pracujący nad projektem SaaStr prowadzonym przez Jasona Lemkina, usunął produkcyjną bazę danych mimo zarządzonego zamrożenia zmian. Według relacji The Register agent wygenerował też około 4000 fikcyjnych rekordów, podawał nieprawdziwe wyniki testów i twierdził, że przywrócenie danych jest niemożliwe, co okazało się nieprawdą. Agent sam nazwał to, co zrobił, „a catastrophic error of judgement”.
Problemem nie była inteligencja modelu. Problemem było to, że agent miał uprawnienia, przy których w pętli nie było niczyjego osądu.
Branża ma już na to słownik. OWASP Top 10 for LLM Applications 2025 opisuje ryzyko Excessive Agency i rozbija je na trzy przyczyny: nadmierną funkcjonalność, nadmierne uprawnienia i nadmierną autonomię. Jednym z zalecanych zabezpieczeń jest wymaganie akceptacji człowieka przed działaniami o dużym wpływie. Willison nazywa „śmiertelną triadą” połączenie trzech cech: dostęp do prywatnych danych, kontakt z niezaufaną treścią i możliwość komunikacji na zewnątrz. Meta zaproponowała regułę Agents Rule of Two: agent bez nadzoru powinien mieć najwyżej dwie z trzech cech — przetwarzanie niezaufanego wejścia, dostęp do wrażliwych danych lub systemów i możliwość zmiany stanu albo komunikacji na zewnątrz. Jeśli potrzebuje wszystkich trzech, potrzebuje też człowieka w pętli.
Jak to rozwiązuje moduł harness w GoatCMS
Moduł harness dodaje do panelu administracyjnego asystenta AI, który pracuje na danych aplikacji. Najciekawsze są w nim decyzje o tym, czego asystent zrobić nie może.
| Ryzyko według OWASP LLM06 | Odpowiedź w module harness |
| Nadmierna funkcjonalność | dla każdej udostępnionej encji powstają trzy wygenerowane narzędzia: query_<encja>, open_<encja>_form i confirm_delete_<encja>; agent nie wykonuje kodu ani dowolnego SQL |
| Nadmierne uprawnienia | efektywny dostęp to koniunkcja maski roli zalogowanego użytkownika i maski modułu harness danej encji |
| Nadmierna autonomia | agent nigdy sam nie zapisuje ani nie usuwa danych; otwiera formularz lub okno potwierdzenia, a decyzję podejmuje człowiek |
AI jest kolejnym użytkownikiem aplikacji, a nie kontem serwisowym. Agent działa z uprawnieniami osoby, która z nim rozmawia. Nie zobaczy pól, których jej rola nie może czytać, nawet jeśli encja udostępnia je asystentowi. Nie zobaczy też pól spoza listy udostępnionej asystentowi, nawet jeśli rola może je czytać. Żadna z dwóch masek osobno nie wystarcza.
Ta decyzja jest zapisana w modelu aplikacji, a nie w prompcie:
module:add --name=harness \
--property:list:list="username,email" \
--property:list:persist="role,username,email,phone,shipping_recipient,shipping_street,shipping_unit,shipping_postal_code,shipping_city,shipping_country,billing_recipient,billing_street,billing_unit,billing_postal_code,billing_city,billing_country"Zbiór persist, czyli pola, które asystent może wstępnie wypełnić w formularzu, celowo pomija password i email_verified_at. Nawet jeśli rola użytkownika pozwala zapisywać hasło, agent nie może zaproponować wartości tego pola. Nikt nie musi o tym pamiętać przy każdym review. Generator wymusza to za każdym razem.
Odczyt jest wąski i parametryzowany. Narzędzie query_<encja> jest tylko do odczytu. Nazwy tabel i kolumn pochodzą z wygenerowanego rejestru i są walidowane, a wartości podane przez model trafiają do SQL wyłącznie jako parametry zapytania. Filtry obsługują tylko równość, a wynik jest ograniczony liczbą wierszy. Jedna tura rozmowy może wykonać najwyżej pięć rund narzędzi odczytu, więc model nie zapętli się w nieskończoność.
Zmiana stanu zawsze przechodzi przez człowieka. Narzędzia frontendowe nie wykonują się na serwerze. Zamieniają prośbę agenta w akcję przeglądarki: wstępnie wypełniony formularz albo okno potwierdzenia usunięcia z prawdziwymi danymi rekordu. W jednej turze może wystąpić najwyżej jedna taka akcja. Gdy agent o nią prosi, odpowiedź w czacie jest stałym komunikatem serwera, a nie tekstem modelu. Model nie ma więc jak napisać „gotowe, usunąłem”, zanim człowiek cokolwiek potwierdzi. W incydencie Replit właśnie fałszywe raporty agenta okazały się jednym z najgroźniejszych elementów.
Brak dostępu nie zdradza istnienia danych. Jeśli rekord nie istnieje albo żadne jego pole nie jest dostępne, podgląd jest po prostu pusty. Podanie identyfikatora cudzej rozmowy działa tak, jakby taka rozmowa nie istniała. To ten sam wzorzec, którego zabrakło w przykładzie z podatnym endpointem zamówień.
Zasady „nie zmieniaj danych” i „nie wymyślaj nazw pól ani identyfikatorów” są także w prompcie systemowym. Tam pełnią jednak rolę instrukcji dla modelu, a nie granicy bezpieczeństwa. Granicą jest kod, który wykonuje tylko to, na co pozwalają maski.
Osąd, którego nie da się zautomatyzować
Uczciwie trzeba powiedzieć, czego ten projekt nie rozwiązuje.
Wyniki narzędzi odczytu trafiają do dostawcy modelu jako część rozmowy. Dodanie pola do listy list jest więc decyzją o udostępnieniu go zewnętrznemu dostawcy AI. Żaden generator nie podejmie jej za zespół.
Dane w rekordach mogą też pochodzić od klientów, na przykład z uwag do zamówienia. To niezaufane wejście, które może próbować sterować sugestiami modelu. Właśnie dlatego każda zmiana kończy się w formularzu, który człowiek widzi i zatwierdza.
Wreszcie: ograniczenia mają koszt. Brak zakresów i sortowania w filtrach oraz jedna akcja na turę sprawiają, że asystent jest mniej „magiczny”. To świadomie nudny projekt. Nudne systemy łatwiej przewidzieć.
W takim modelu AI jest asystentem użytkownika, a nie jego zastępcą. Przygotowuje pracę: wyszukuje rekordy, wypełnia formularz, pokazuje, co zostanie usunięte. Decyzję podejmuje człowiek. To cała teza tego artykułu w miniaturze: wygenerowanie propozycji jest tanie, zatwierdzenie jej ma wartość, bo stoi za nim odpowiedzialność.
Juniorzy: nie ucz się promptowania zamiast rozumienia
Najczęstsza rada dla początkujących brzmi dziś: „naucz się dobrze promptować”. To przydatna umiejętność, ale opanowuje się ją w tygodnie. Rozumienie systemów buduje się latami. I to właśnie ono decyduje, czy potrafisz powiedzieć AI, że wygenerowany kod jest zły.
W styczniu 2026 roku badacze Anthropic opublikowali eksperyment z udziałem 52 głównie początkujących programistów, którzy uczyli się nieznanej biblioteki asynchronicznej Trio. Połowa mogła korzystać z asystenta AI. W teście zrozumienia, rozwiązywanym już bez AI, grupa z asystentem uzyskała średnio 50%, a grupa pisząca samodzielnie 67%. Grupa z AI nie była przy tym istotnie statystycznie szybsza. Największa różnica dotyczyła pytań o debugowanie.
Najważniejszy jest jednak inny wynik. Uczestnicy, którzy pytali AI o koncepcje i prosili o wyjaśnienia, osiągali 65% i więcej. Ci, którzy w pełni delegowali pisanie kodu, poniżej 40%. Narzędzie było to samo. Różnił się sposób korzystania z niego.
Badanie Microsoft Research i Carnegie Mellon z 2025 roku, oparte na ankiecie wśród 319 pracowników umysłowych, pokazało podobny mechanizm: im większe zaufanie do AI, tym mniej krytycznego myślenia. Im większa pewność własnych kompetencji, tym więcej.
Ten paradoks opisuje już klasyczna praca Lisanne Bainbridge „Ironies of Automation” z 1983 roku. Automatyzacja zostawia człowiekowi najtrudniejsze zadania: nadzór i interwencję, gdy coś pójdzie nie tak. Jednocześnie odbiera mu codzienną praktykę, dzięki której mógłby nabrać do tego kompetencji.
Dlatego warto zakwestionować samo otwarcie tego tekstu. Pisanie kodu przestaje być najcenniejszą umiejętnością. Ale nadal jest najlepszym sposobem nauki oceniania kodu.
Praktycznie:
- Najpierw spróbuj rozwiązać problem samodzielnie, a dopiero potem porównaj swoje rozwiązanie z wersją AI. Różnice są materiałem do nauki.
- Pytaj AI „dlaczego?” i „co może tu pójść źle?”, a nie tylko „napisz”.
- Regularnie debuguj bez agenta: stack trace, debugger, logi, profiler.
- Ucz się rzeczy, których nie widać w pojedynczym pliku: modelowania danych, transakcji i poziomów izolacji, HTTP, uwierzytelniania i autoryzacji, logów, metryk i tracingu.
- Czytaj publiczne postmortemy awarii. To skondensowany osąd ludzi, którzy zapłacili za swoje błędy.
- Stosuj regułę Willisona: nie commituj kodu, którego nie potrafisz wyjaśnić.
Dla liderów zespołów wniosek jest symetryczny: dawajcie juniorom review zmian przygotowanych przez AI razem z seniorem, a nie tylko generowanie kolejnych. I mierzcie zrozumienie, a nie liczbę linii.
Gdzie ta teza może się mylić
Warto sprawdzić własne założenia.
Modele szybko się poprawiają. Nowsze dane METR sugerują realne przyspieszenie, a lepsze modele i narzędzia do AI review będą wyłapywać coraz więcej problemów z tego tekstu, na przykład brak autoryzacji czy naiwne retry. To prawda. Ale to przesuwa osąd wyżej, a nie go eliminuje. To, co jest „poprawne”, zależy od kontekstu biznesowego, apetytu na ryzyko i odpowiedzialności, których nie ma w repozytorium.
Fundamenty nigdy nie zniknęły. Teza o ich „powrocie” jest uproszczeniem. Zmieniła się proporcja cen. Dobry inżynier systemów zawsze był cenny. Nowe jest to, że różnicy między nim a szybkim producentem kodu nie da się już ukryć za tempem pisania.
„Osąd” może stać się wymówką. Senior, który blokuje każdy PR, bo „ma przeczucie”, nie jest wąskim gardłem jakości, tylko po prostu wąskim gardłem. Osąd, którego nie da się wyjaśnić i zapisać w teście, ograniczeniu, uprawnieniu czy decyzji architektonicznej, nie skaluje się i nie da się go zweryfikować.
Odpowiedzialność się nie kompresuje
AI kompresuje czas potrzebny do napisania kodu. Nie kompresuje odpowiedzialności za decyzje.
Kiedy o trzeciej w nocy produkcja leży, nikt nie pyta, który model wygenerował retry. Pyta, kto je zatwierdził i dlaczego.
Dlatego najlepsze zespoły nie będą tymi, które generują najwięcej kodu. Będą tymi, które najlepiej wiedzą, czego nie wpuścić do systemu, i które zapisują tę wiedzę tak, żeby nie trzeba było jej odkrywać przy każdym PR-ze.
Im tańszy staje się kod, tym droższy staje się dobry judgment.
A teraz pytanie do Ciebie: jaki był najbardziej przekonujący, a zarazem zły kod, który wygenerowało dla Ciebie AI? I po czym było widać, że jest zły? Takie historie uczą więcej niż benchmarki.
Źródła
Ekonomia kodu i rola człowieka
- Simon Willison: Writing code is cheap now, Agentic Engineering Patterns, 2026
- Simon Willison: Not all AI-assisted programming is vibe coding, 2025
- Simon Willison: Vibe engineering, 2025
- Kent Beck: 90% of My Skills Are Now Worth $0, 2023
- Joel Spolsky: Strategy Letter V, 2002
- Frederick P. Brooks: No Silver Bullet — Essence and Accident in Software Engineering, 1986
- Peter Naur: Programming as Theory Building, Microprocessing and Microprogramming 15, 1985
- Brian W. Kernighan, P. J. Plauger: The Elements of Programming Style, wyd. 2, 1978
Produktywność i jakość kodu
- METR: Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity, 2025
- METR: We are Changing our Developer Productivity Experiment Design, 2026
- Faros AI: The AI Productivity Paradox Research Report, 2025
- DORA: Accelerate State of DevOps Report 2024
- Google Cloud: Announcing the 2025 DORA Report
- Stack Overflow Developer Survey 2025
- CodeRabbit: State of AI vs Human Code Generation Report, 2025
- GitClear: AI Copilot Code Quality, 2025
- OX Security: Army of Juniors, 2025
- Addy Osmani: How AI-assisted coding will change software engineering: hard truths
- Addy Osmani: Comprehension Debt — the hidden cost of AI generated code
- Thoughtworks Technology Radar: Complacency with AI-generated code
Bezpieczeństwo i niezawodność
- Veracode: 2025 GenAI Code Security Report
- Spracklen i in.: We Have a Package for You! A Comprehensive Analysis of Package Hallucinations by Code Generating LLMs, USENIX Security 2025
- OWASP API Security Top 10 2023: API1 Broken Object Level Authorization
- Google SRE Book: Addressing Cascading Failures
Agenci, uprawnienia i harness engineering
- OWASP Top 10 for LLM Applications: LLM06:2025 Excessive Agency
- Simon Willison: The lethal trifecta for AI agents, 2025
- Meta AI: Agents Rule of Two — A Practical Approach to AI Agent Security, 2025
- The Register: Replit deleted user's production database, 2025
- Fortune: Amazon outages, AI-assisted changes and Amazon's response, 2026
- OpenAI: Harness engineering — leveraging Codex in an agent-first world, 2026
- Birgitta Böckeler: Harness Engineering — first thoughts, martinfowler.com, 2026
Nauka i rozwój umiejętności
- Anthropic: How AI assistance impacts the formation of coding skills, 2026
- Judy Hanwen Shen, Alex Tamkin: pełna publikacja na arXiv
- Microsoft Research: The Impact of Generative AI on Critical Thinking, CHI 2025
- Lisanne Bainbridge: Ironies of Automation, Automatica 19, 1983