Programowanie sterowane błędami
Programowanie sterowane błędami to sposób pracy z agentami AI, w którym każdy błąd kompilatora, nieprzechodzący test lub wyjątek zgłoszony w czasie działania staje się kolejną specyfikacją dla agenta. Agent naprawia przyczynę źródłową pierwszego błędu, uruchamia ponownie to samo sprawdzenie i powtarza. Pętla jest wiarygodna dopiero wtedy, gdy końcowa, nietknięta bramka dowodzi, że się zbiegła.
Ta strona jest dla deweloperów, którzy na co dzień pracują z agentami, i dla tech leadów, którzy chcą, żeby pętla działała tak samo w całym zespole. Zakłada, że masz polecenie sprawdzające, które wykonuje się w kilka sekund; jeśli jeszcze go nie masz, najpierw daj sesji pętlę informacji zwrotnej.
CI świeci na czerwono: 14 błędów TypeScript, trzy nieprzechodzące testy i stack trace wskazujący na payment.ts:212. Spędziłeś dwadzieścia minut na zgadywaniu, który z ostatnich sześciu commitów to zepsuł, a twoja ostatnia poprawka zamieniła jeden czerwony test w trzy. Wklejenie całego loga z dopiskiem „napraw wszystko” tylko pogarsza sprawę, bo agent łata objawy równolegle, a każda łatka rodzi nowe błędy.
Co daje programowanie sterowane błędami
Dział zatytułowany „Co daje programowanie sterowane błędami”- Sześciokrokową pętlę, którą prowadzisz tak samo w Claude Code, Codeksie i Cursorze, interaktywnie albo w trybie headless
- Gotowe prompty na produkcyjny stack trace, kaskadę kompilatora, pętlę z nieprzechodzącym testem i pętlę, która utknęła
- Bramkę z czterech sprawdzeń, która dowodzi, że pętla zbiegła się uczciwie, więc nikt nie musi czytać każdej zmienionej linii
- Wyjście awaryjne, gdy naprawa jednego błędu wciąż rodzi trzy kolejne
- Konfigurację serwera MCP Sentry, która zastępuje wklejanie stack trace’ów jednym ID zgłoszenia
- Nawyk zamiany trudno wywalczonej poprawki w sprawdzenie albo regułę, żeby ten sam błąd już nie wrócił
Jak działa pętla błąd-naprawa-ponowne uruchomienie
Dział zatytułowany „Jak działa pętla błąd-naprawa-ponowne uruchomienie”- Wprowadź zmianę albo pozwól agentowi ją wprowadzić.
- Uruchom polecenie weryfikujące: type-check, lint, odpowiednie testy albo build.
- Weź tylko pierwszy błąd.
- Przekaż go agentowi razem z plikami, które wskazuje, i jednym zdaniem o tym, co kod ma robić.
- Poproś o przyczynę źródłową i poprawkę, a nie o wyciszenie błędu.
- Uruchom ponownie dokładnie to samo polecenie z kroku 2. Powtarzaj, aż przejdzie, a potem uruchom końcową bramkę opisaną w sekcji jak udowodnić, że pętla się zbiegła.
Dyscyplina, dzięki której pętla działa, to jeden błąd naraz. Błędy kaskadują: jeden zły typ daje dziesiątki błędów pochodnych, a naprawa pierwszego często usuwa wiele pozostałych. Naprawianie błędu nr 14, gdy wciąż istnieje błąd nr 1, marnuje cykl i często wprowadza sprzeczną poprawkę.
Druga dyscyplina to limit. Nieograniczona pętla na sprawdzeniu, którego nie da się spełnić, spala kontekst i pieniądze. Ogranicz liczbę prób w prompcie, a w uruchomieniach headless dodaj też limit zewnętrzny: flagę kosztu, jeśli narzędzie ją ma (--max-budget-usd w Claude Code), albo limit czasu na job, jeśli jej nie ma.
Jak przekazać błąd do Claude Code, Codeksa i Cursora
Dział zatytułowany „Jak przekazać błąd do Claude Code, Codeksa i Cursora”Cykl jest w każdym narzędziu ten sam: pokaż błąd, dodaj kontekst, napraw i uruchom ponownie polecenie, które zawiodło. Narzędzia różnią się tym, kto uruchamia polecenie, jak prowadzisz pętlę bez nadzoru i jak wycofujesz złą poprawkę.
W sesji interaktywnej Claude Code uruchamia sprawdzenie przez narzędzie Bash, czyta wynik i iteruje, a ty niczego nie kopiujesz. Wklej do promptu:
Run npm run type-check. Fix the FIRST error only: read the file and line itnames, find the root cause, fix it, and re-run npm run type-check. Repeat.Never add @ts-ignore, @ts-expect-error or `as any`. After 6 attempts, or ifthe same error comes back twice, stop and report what you tried.Żeby uruchomić tę samą pętlę bez nadzoru, z terminala albo w jobie CI, użyj trybu print z jawnym limitem i wąskim zestawem dozwolonych narzędzi:
claude -p "Run npm run type-check and fix the errors one at a time, root cause first. Never suppress an error. Stop when it passes." \ --max-budget-usd 2 \ --permission-mode acceptEdits \ --allowedTools "Bash(npm run type-check)"--max-budget-usd zatrzymuje pętlę, która nie może się zbiec, bo ogranicza kwotę, jaką może wydać. acceptEdits pozwala agentowi edytować pliki bez pytania, a --allowedTools ogranicza go do jednego potrzebnego polecenia. Prompt umieść zaraz po -p: --allowedTools przyjmuje wiele wartości, więc prompt podany po nim zostanie odczytany jako kolejna nazwa narzędzia i uruchomienie zakończy się bez danych wejściowych. Tryb print pomija okno zaufania do workspace’u, więc uruchamiaj go tylko w katalogu, któremu ufasz. W sesji interaktywnej cofnij złą poprawkę przez /rewind (albo Esc Esc).
Codex uruchamia polecenia w swoim sandboksie. Do pętli interaktywnej codex --sandbox workspace-write pozwala mu edytować workspace i uruchamiać sprawdzenie; domyślna polityka zatwierdzania, on-request, pyta cię o wszystko, co wychodzi poza sandbox. on-request nie zatrzyma za ciebie nieudanej pętli, więc limit wpisz w prompt:
Run npm run type-check. Fix the first error only, then re-run the check tosee which downstream errors disappeared. One root cause per iteration.Do not suppress errors. After 6 attempts, stop and report.W uruchomieniu headless codex exec przyjmuje ten sam prompt:
codex exec --sandbox workspace-write \ "Run npm run type-check and fix the errors one at a time, root cause first. Never suppress an error. Stop after 6 attempts and report."codex exec nie ma flagi limitu tur ani wydatków (sprawdzone w 0.157.1), więc limit zapisujesz w prompcie; w CI dodatkowo obejmij job limitem czasu, na przykład timeout 15m codex exec … albo timeout-minutes joba. Złą poprawkę cofasz Gitem, a gdy pętla kręci się w kółko, zaczynasz czysty wątek przez /new.
Agent Cursora uruchamia sprawdzenie we własnym terminalu, czyta wynik i iteruje. Po nieudanym poleceniu możesz poprosić go o naprawę błędu widocznego w terminalu. Gdy wklejasz błąd sam, dodaj kontekst, dzięki któremu agent będzie wiedział, co naprawić:
I ran npm run type-check and got this error:
src/services/notification.ts:42:5 - error TS2345:Argument of type 'string' is not assignable to parameterof type 'NotificationPayload'.
Fix the root cause. The function in @src/services/notification.ts expectsa NotificationPayload object, not a raw string. Check the caller at line 42and the type in @src/types/notification.ts. Do not cast.Commituj przy każdym zielonym stanie, żeby złą poprawkę cofnąć jednym git restore. Cursor sprawdza się, gdy chcesz obserwować pętlę i wkroczyć, gdy tylko zacznie zbaczać. Żeby prowadzić pętlę bez nadzoru, przekaż ją Cloud Agentowi Cursora albo uruchamiaj go z Automation, na przykład po alercie z Sentry albo według harmonogramu.
Trzy sytuacje, w których pętla się opłaca
Dział zatytułowany „Trzy sytuacje, w których pętla się opłaca”Napraw błąd produkcyjny na podstawie stack trace’u
Dział zatytułowany „Napraw błąd produkcyjny na podstawie stack trace’u”Użytkownik trafił na crash, a error tracker go przechwycił. Daj agentowi pełny wyjątek i cały stos, a nie samą górną linię: górna ramka to miejsce, w którym wartość została odczytana, a rzadko to, w którym coś poszło źle.
Właściwa poprawka sięga tam, gdzie total stało się undefined (na przykład do koszyka utworzonego bez pozycji), a nie dokleja operatora ?. w linii 212. To test regresyjny pozwala udowodnić, że poprawka działa: nie przechodzi przed zmianą i przechodzi po niej.
Z podłączonym serwerem MCP Sentry agent sam pobiera zgłoszenie, stack trace i breadcrumbs, a prompt kurczy się do „fetch Sentry issue PROJ-1234 and fix the root cause”. Oficjalny serwer Sentry działa zdalnie pod https://mcp.sentry.dev/mcp i loguje przez OAuth:
claude mcp add --transport http sentry https://mcp.sentry.dev/mcpcodex mcp add sentry --url https://mcp.sentry.dev/mcpcodex mcp login sentryDodaj serwer do .cursor/mcp.json:
{ "mcpServers": { "sentry": { "url": "https://mcp.sentry.dev/mcp" } } }Jeśli uwierzytelniasz się tokenem zamiast OAuth, nagłówek to Authorization: Sentry-Bearer <token>, a nie Bearer. Debugowanie produkcji z edytora omawia Sentry razem z Grafaną, Datadogiem i PostHogiem.
Przejdź przez kaskadę kompilatora po refaktoryzacji
Dział zatytułowany „Przejdź przez kaskadę kompilatora po refaktoryzacji”Zmieniłeś sygnaturę kluczowej funkcji i type-checker zgłasza 30 błędów w całej bazie kodu. To najmocniejszy przypadek tej pętli, bo błędy tworzą dokładną, wygenerowaną maszynowo listę zadań. Nie naprawiaj niczego ręcznie na starcie; poczekaj, aż pojawi się pełna lista, i wtedy ją oddaj.
Polecenie zatrzymania się przy wywołaniach bez waluty ma znaczenie: zamienia zgadywanie, które agent zrobiłby po cichu, w krótką listę decyzji dla ciebie.
Doprowadź nieprzechodzący test do zieleni
Dział zatytułowany „Doprowadź nieprzechodzący test do zieleni”Najmocniejsza forma pętli zaczyna się od testu napisanego jako pierwszy, bo test jest wyrocznią, którą agent może uruchamiać, ale nie może zmieniać. Programowanie sterowane testami opisuje pisanie takiego testu; poniższy prompt to druga, sterowana błędami połowa.
Ostatnie zdanie ma znaczenie. Bez niego agent czasem „naprawia” awarię, luzując asercję. Prompt to prośba, a nie mechanizm kontroli, więc gdy stawka jest wyższa, chroń wyrocznię sprawdzeniami, których agent nie może edytować.
Jak udowodnić, że pętla się zbiegła?
Dział zatytułowany „Jak udowodnić, że pętla się zbiegła?”Zielone ponowne uruchomienie polecenia, które zawiodło, dowodzi mniej, niż się wydaje. Agent mógł wyciszyć błąd, pominąć test, osłabić asercję albo naprawić jedno sprawdzenie, psując inne. Zanim przyjmiesz wynik, przejdź przez tę bramkę. Każde sprawdzenie jest mechaniczne, więc nikt nie musi czytać każdej zmienionej linii.
-
Uruchom od nowa wszystkie sprawdzenia, a nie tylko to, które zawiodło. Na przykład
npm run type-check && npm run lint && npm test. Tu wychodzi poprawka typu, która zepsuła test w innym miejscu. -
Przeszukaj diff pod kątem wyciszeń. Każde trafienie wymaga pisemnego uzasadnienia albo wycofania:
Okno terminala git diff main -U0 | grep -nE '^\+.*(@ts-ignore|@ts-expect-error|as any|eslint-disable|\.skip\(|\.only\(|catch( \([^)]*\))? *\{ *\})' -
Potwierdź, że testów nie zmieniono, żeby przeszły. To polecenie wypisuje tylko usunięte lub zmienione linie w plikach testów i snapshotach. Nie powinno niczego wypisać: dodane testy są w porządku, usunięte lub zmienione linie – nie.
Okno terminala git diff main -U0 -- '*.test.*' '*.spec.*' '*__snapshots__*' | grep -E '^-[^-]' -
Udowodnij, że każdy test regresyjny łapie błąd. Przywróć stary kod źródłowy, zostaw nowy test i sprawdź, że kończy się porażką. Najpierw zacommituj poprawkę, a potem uruchom na przykład:
git checkout main -- src/services/payment.ts && npx vitest run src/services/payment.test.ts; git checkout HEAD -- src/services/payment.ts. Tutaj test musi zakończyć się porażką; ostatnie polecenie przywraca poprawiony plik. Test regresyjny, który przechodzi na starym kodzie, niczego nie testuje.
Deweloper, który prowadził pętlę, zatwierdza wyjaśnienie przyczyny źródłowej i każde trafienie z kroków 2 i 3. CI uruchamiające te same sprawdzenia jest bramką przed merge’em. Żeby przenieść kroki 1–3 z promptu do harnessu, użyj hooka Stop w Claude Code, który je uruchamia i kończy się kodem 2 przy porażce: agent pracuje dalej, zamiast ogłaszać zwycięstwo. Hook, który zawsze kończy się porażką, każe agentowi pracować bez końca, więc niech sprawdza stop_hook_active (albo ogranicza liczbę ponowień), a w uruchomieniach headless zostaw limit --max-budget-usd. Zobacz hooki jako deterministyczne zabezpieczenia. Codex też ma zdarzenie hooka Stop.
Jak przerwać kaskadę, która się nie zbiega
Dział zatytułowany „Jak przerwać kaskadę, która się nie zbiega”Lista zadań z kompilatora kurczy się w miarę pracy. Groźna kaskada robi odwrotnie: naprawa błędu 1 wprowadza błąd 2, jego naprawa wprowadza błąd 3, a kontekst zapełnia się nieudanymi próbami. Sygnałem jest ten sam plik w każdej rundzie. Wtedy przestań naprawiać błędy i przyjrzyj się strukturze.
Użyj /rewind, żeby wrócić do ostatniego dobrego stanu, albo /clear, żeby porzucić zanieczyszczony kontekst, a potem zacznij od promptu analitycznego poniżej. Po dwóch nieudanych cyklach poprawek tego samego błędu świeży kontekst zwykle radzi sobie lepiej niż dłuższy, bo każda nieudana próba zostaje w oknie i steruje następną.
Przywróć pliki Gitem i zacznij czysty wątek przez /new. Wklej prompt analityczny poniżej, żeby nowy wątek wystartował od struktury, a nie od ostatniej nieudanej łatki.
Przywróć ostatni zielony commit, otwórz nowy czat i wklej prompt analityczny poniżej z odwołaniami @ do pliku, jego typów i pliku testów. Poproś o propozycję przed jakąkolwiek edycją.
Jak sprawić, żeby ten sam błąd nie wrócił
Dział zatytułowany „Jak sprawić, żeby ten sam błąd nie wrócił”Najcenniejsze błędy to te, których więcej nie zobaczysz. Gdy błąd był trudny do zdiagnozowania, zapisz lekcję w najmocniejszej dostępnej formie:
- Sprawdzenie, które zawodzi deterministycznie: reguła lintera, typ, test albo krok CI. Działa dla każdego agenta i każdej osoby, niezależnie od tego, czy czytają instrukcje.
- Instrukcja projektu, gdy lekcji nie da się wyrazić jako sprawdzenia. Krótka i konkretna.
Dodaj lekcję do projektowego CLAUDE.md, żeby dostał ją cały zespół. Auto memory Claude Code też zapisuje wnioski w ~/.claude/projects/<project>/memory/, ale ten katalog należy do jednego użytkownika i nie jest współdzielony przez repozytorium.
Dodaj lekcję do AGENTS.md, który Codex czyta przed rozpoczęciem jakiejkolwiek pracy w zaufanym projekcie.
Dodaj regułę projektu w .cursor/rules/ (Rules w Cursorze), która obowiązuje w każdym zapytaniu.
Treść jest taka sama we wszystkich trzech plikach:
## Known error patterns
- Notification types: update src/types/notification.ts AND the Zod schema in src/validators/notification.ts together. They must stay in sync.- Integration tests need Redis: run `docker compose up -d redis` first.O tym, jak przechowywać i przycinać długotrwałe lekcje, piszemy w wzorcach pamięci.
Kiedy programowanie sterowane błędami zawodzi
Dział zatytułowany „Kiedy programowanie sterowane błędami zawodzi”- Agent goni niewłaściwy błąd. Naprawia błąd nr 14, który wynika z błędu nr 1. Wyjście: powtórz „napraw tylko pierwszy błąd, potem uruchom ponownie” i odrzuć poprawkę spoza kolejności.
- Poprawka leczy objaw.
?.zatrzymuje crash, ale nie wyjaśnia, dlaczego wartości brakowało. Wyjście: zapytaj „gdzie ta wartość stała się undefined?” i wymagaj testu regresyjnego, który nie przechodzi na starym kodzie. - Agent wycisza zamiast naprawiać.
@ts-ignore,as any,eslint-disablei puste bloki catch ukrywają błąd. Wyjście: wyłapie je skanowanie wyciszeń w bramce; wycofaj zmianę i ponów prompt z wyraźnym zakazem. - Test poprawiono, żeby przeszedł. Wyjście: wyłapie to sprawdzenie plików testów w bramce; przywróć test z Gita i zablokuj agentowi jego edycję sprawdzeniami, których nie może zmienić.
- Pętla działa na niestabilnym teście. Niedeterministyczna awaria pozwala agentowi „naprawić”, przypadkiem zobaczyć zieleń i ogłosić zwycięstwo. Wyjście: najpierw uruchom test kilka razy; jeśli jest niestabilny, odizoluj go przed startem pętli.
- Wklejka ukrywa winowajcę. Podano tylko górną ramkę stosu. Wyjście: wklej pełny trace albo pobierz go przez serwer MCP Sentry i wskaż podejrzane pliki.
- Setki błędów naraz. Wyjście: napraw najpierw pierwsze trzy–pięć błędów, bo zwykle to one są przyczynami źródłowymi, a potem uruchom ponownie i sprawdź, ile zostało.
- Błąd nie niesie informacji. Segfault albo gołe „internal server error” to nie specyfikacja. Wyjście: przejdź na debugowanie sterowane hipotezami: odtwórz problem, dodaj logowanie, zawężaj przez eliminację. Skill
systematic-debuggingz obra/superpowers zapisuje ten proces (npx skills add obra/superpowers --skill systematic-debugging); przeczytaj jegoSKILL.mdprzed instalacją, bo katalog skilla zawiera też fikstury testowe. Zobacz skille do testowania i debugowania. - Zepsuty plik agent napisał od zera. Wyjście: usuń go i wygeneruj ponownie z ciaśniejszego promptu, który zawiera błąd; zwykle to lepsze niż łatanie.