Wzorce debugowania
Twój endpoint uwierzytelniania zawodzi przy mniej więcej 30% logowań na produkcji. Ślad stosu wskazuje na linię, która wygląda poprawnie, błąd znika w chwili, gdy podłączysz debugger, a twój PM chce szacowanego czasu naprawy. To rodzaj błędu, który pożera całe popołudnie. AI też nie zna magicznie odpowiedzi — ale użyte systematycznie zmienia frustrujące polowanie w zwartą pętlę: instrumentuj, odtwórz, skoreluj, napraw.
Co z tego wyniesiesz
Dział zatytułowany „Co z tego wyniesiesz”- Prompt wielokrotnego użytku do instrumentowania sporadycznych błędów strategicznym logowaniem
- Przepływ pracy zmieniający surowy produkcyjny ślad stosu w defensywną poprawkę
- Gotowy prompt do napisania niezaliczonego testu warunku wyścigu, zanim naprawisz błąd
- Przepis na śledzenie między usługami korelujący logi po ID żądania
- Tryby awarii debugowania z pomocą AI — i jak utrzymać je w ryzach
Wzorzec 1: Pętla strategicznego logowania
Dział zatytułowany „Wzorzec 1: Pętla strategicznego logowania”Wzorzec o największej dźwigni: pozwól AI zinstrumentować podejrzaną ścieżkę, odtworzyć problem pod obciążeniem, a następnie przekaż logi z powrotem do korelacji. Przepływ pracy jest taki sam we wszystkich trzech narzędziach — zmienia się tylko powierzchnia (Cursor edytuje plik w edytorze, Claude Code działa headless w repozytorium, Codex działa w TUI lub Cloud).
-
Opisz problem precyzyjnie. Niejasne dane wejściowe dają niejasne logowanie. Podaj objaw, częstotliwość i to, co już wykluczyłeś.
-
Pozwól narzędziu dodać logowanie. Ten sam prompt napędza każde narzędzie:
Otwórz
auth.js, zaznacz funkcjęvalidateTokeni uruchom prompt w trybie Agent (Cmd/Ctrl+I). Przejrzyj różnicę inline przed akceptacją. Cursor dodaje ukierunkowane, strukturalne logowanie:async function validateToken(token) {console.log('[AUTH] validation started', {tokenLength: token?.length,at: Date.now(),});try {const decoded = jwt.verify(token, SECRET);const msToExpiry = decoded.exp * 1000 - Date.now();console.log('[AUTH] decoded', { userId: decoded.userId, msToExpiry });if (msToExpiry < 60_000) {console.warn('[AUTH] token expiring soon', { msToExpiry });}return decoded;} catch (error) {console.error('[AUTH] validation failed', {message: error.message,iat: jwt.decode(token)?.iat,});throw error;}}Uruchom prompt headless w katalogu głównym repozytorium, następnie odtwórz problem i przekaż logi z powrotem:
Okno terminala claude "Add strategic debug logging to auth.js to diagnose intermittent token validation failures. Instrument timing, clock skew, and race conditions. Logging only."# Odtwórz pod obciążeniem, przechwyć wyjścieNODE_ENV=debug npm start 2>&1 | tee debug.log# Przekaż przechwycone logi z powrotem do korelacjiclaude "Analyze debug.log and identify what correlates with the [AUTH] validation failures"Uruchom TUI z promptem albo uruchom go headless przez
codex exec:Okno terminala codex "add strategic debug logging to auth.js for intermittent token validation failures: timing, clock skew, race conditions. logging only"# Albo nieinteraktywnie do CI / odtwarzania skryptowegocodex exec "instrument auth.js token validation with structured [AUTH] logging, no behavior change" \--sandbox workspace-write -
Odtwórz pod obciążeniem, aby ujawnić błąd zależny od czasu.
Okno terminala # Bombarduj ścieżkę, aby sporadyczny błąd faktycznie wystąpiłnpm test -- --grep "authentication" --repeat 100 2>&1 | tee debug.log -
Przekaż logi z powrotem i poproś o korelację, nie o zgadywanie.
Dobra odpowiedź zawęża zakres do testowalnej przyczyny — np. „błędy gromadzą się, gdy opóźnienie walidacji spycha
msToExpiryna wartość ujemną; token wygasa w trakcie żądania. Wtórny sygnał: przesunięcieiato ~4s między dwoma hostami”. Teraz naprawiasz tolerancję dryftu zegara i wyprzedzające odświeżanie w oparciu o dowody, a nie o przeczucie.
Wzorzec 2: Od produkcyjnego śladu stosu do defensywnej poprawki
Dział zatytułowany „Wzorzec 2: Od produkcyjnego śladu stosu do defensywnej poprawki”Surowy ślad stosu z Sentry lub twoich logów to najbogatsze pojedyncze dane wejściowe, jakie możesz przekazać AI — przypina plik, linię i łańcuch wywołań. Zadanie polega na zmianie go w poprawkę, która obsługuje przypadek brzegowy bez zamiatania prawdziwej przyczyny pod dywan.
Rozróżnienie w tym ostatnim zdaniu to właśnie to, co powstrzymuje AI od wyciszenia prawdziwego błędu. Dobra odpowiedź rozdziela te dwa przypadki:
async processOrder(userId, orderData) { // "Should never happen" -> fail loudly if (!userId) throw new ValidationError('User ID required');
const user = await this.getUser(userId); if (!user) throw new NotFoundError(`User ${userId} not found`);
// "Expected sometimes" -> degrade gracefully if (!user.stripeCustomer?.id) { logger.warn('User missing Stripe customer; creating one', { userId }); user.stripeCustomer = await this.createStripeCustomer(user); } return this.createOrder(user, orderData);}Wzorzec 3: Wykrywanie wycieków pamięci
Dział zatytułowany „Wzorzec 3: Wykrywanie wycieków pamięci”Gdy użycie sterty rośnie bez ograniczeń, zacznij od prawdziwych narzędzi, a potem pozwól AI zinterpretować dowody. Przechwyć zrzut wbudowanym inspektorem Node (node --inspect, następnie zakładka Memory w Chrome DevTools, lub node --heapsnapshot-signal=SIGUSR2), albo uruchom clinic.js (clinic heapprofiler) lub 0x dla wykresu płomieniowego. Przekaż AI rozbicie retained-size, a nie ogólnikowe „przecieka”.
Zwykłym winowajcą, którego ujawni AI, jest nieograniczona kolekcja — nasłuchiwacze, timery lub wpisy cache dodawane, ale nigdy nieusuwane. Poprawka to ścieżka czyszczenia, którą wywołujący faktycznie uruchamiają:
addListener(event, callback) { const listener = { event, callback }; this.listeners.push(listener); // Hand back an unsubscribe so callers can release the reference return () => { const i = this.listeners.indexOf(listener); if (i > -1) this.listeners.splice(i, 1); };}Wzorzec 4: Wykrywanie warunków wyścigu
Dział zatytułowany „Wzorzec 4: Wykrywanie warunków wyścigu”Identyfikuj i naprawiaj błędy związane z czasem. Najsilniejszym ruchem jest zmuszenie AI do napisania niezaliczonego testu, który odtwarza wyścig, zanim dotknie poprawki — inaczej nie odróżnisz, czy poprawka zadziałała.
// AI writes the failing test firstdescribe('Payment Processing Race Conditions', () => { it('should handle concurrent submissions', async () => { const userId = 'test-user'; const paymentData = { amount: 100, currency: 'USD' };
// Simulate rapid clicks const promises = Array(5).fill(null).map(() => processPayment(userId, paymentData) );
const results = await Promise.allSettled(promises);
// Only one should succeed const successful = results.filter(r => r.status === 'fulfilled'); expect(successful).toHaveLength(1);
// Others should be rejected with idempotency error const rejected = results.filter(r => r.status === 'rejected'); expect(rejected).toHaveLength(4); rejected.forEach(r => { expect(r.reason.message).toContain('Payment already processing'); }); });});
// AI suggests idempotency solutionclass PaymentService { constructor() { this.processingPayments = new Map(); }
async processPayment(userId, paymentData) { const idempotencyKey = `${userId}-${Date.now()}`;
// Check if already processing if (this.processingPayments.has(userId)) { throw new ConflictError('Payment already processing'); }
// Mark as processing this.processingPayments.set(userId, idempotencyKey);
try { // Process payment const result = await this.chargeCard(paymentData); return result; } finally { // Always cleanup this.processingPayments.delete(userId); } }}Debugowanie między usługami
Dział zatytułowany „Debugowanie między usługami”Gdy błąd obejmuje wiele usług, sygnał żyje w korelacji, a nie w żadnym pojedynczym pliku logu. Przepis: ostempluj ID żądania na brzegu, zbierz logi z każdego przeskoku i pozwól AI odtworzyć oś czasu.
# Zbierz logi z każdego przeskoku (dostosuj do polecenia logów twojej platformy)kubectl logs -l app=user-service --since=1h > user-service.logkubectl logs -l app=payment-service --since=1h > payment-service.log
# Przekaż je do AI w celu odtworzenia osi czasuclaude "Correlate these logs by requestId and trace the failed payment flow for abc-123"Odtworzona oś czasu zmienia „coś jest wolne” w „Payment Service był zablokowany 5s w oczekiwaniu na bazę danych, potem pula się wyczerpała i łańcuch wywołań kaskadowo padł” — przyczynę źródłową, na którą możesz zareagować. Dla bogatszych śladów podłącz spany OpenTelemetry zamiast parsować logi tekstowe; ten sam prompt korelacyjny działa na wyeksportowanych danych spanów.
Gdy to zawodzi
Dział zatytułowany „Gdy to zawodzi”Debugowanie z pomocą AI zawodzi w określony, rozpoznawalny sposób. Znajomość tych trybów odróżnia prawdziwe śledztwo od pewnie brzmiącej ślepej uliczki.
Co dalej
Dział zatytułowany „Co dalej”- Wzorce logowania - strukturalne logowanie, dzięki któremu powyższe prompty korelacyjne trafiają w punkt
- Wzorce monitorowania - złap te błędy, zanim zrobią to użytkownicy
- Wzorce odzyskiwania - łagodna degradacja i ponawianie, gdy już znasz przyczynę