Rzemiosło · The Craft
Czym jest The CraftPoziomyJak zacząćRozdziały Szukaj Pobierz
Level 3 · Kodeks ~4 min czytania

W jednym zdaniu: Co dzieje się po wdrożeniu, gdy realni użytkownicy i zewnętrzne systemy uderzają w serwis — i jak go uodpornić.

14 — Odporność operacyjna i zewnętrzne zależności

Przykazania III, V, VI w warstwie runtime: prod żyje w zawodnym świecie. Sieć rwie, provider blokuje porty, Scraping — Automatyczne zbieranie danych ze stron internetowych przez program zamiast ręcznego kopiowania. Potężne do pozyskiwania danych, ale wymaga kultury: szanuj cudze zasady (robots.txt), nie przeciążaj serwera. pada w połowie, płatne API — Umówiony sposób, w jaki dwa programy gadają ze sobą — jeden prosi, drugi odpowiada w ustalonym formacie. Przez API Twoja aplikacja łączy się z cudzymi usługami (płatności, mapy, AI). Klucz API traktuj jak hasło. kosztuje przy każdym żądaniu. Doktryna z rozdziałów 03–05 dba o kod i wdrożenie; ten rozdział o tym, co dzieje się po — gdy realni użytkownicy i niezależne systemy uderzają w działający serwis.

Te lekcje są drogo opłacone: prawie każda to incydent na prodzie, nie teoria. Wspólny mianownik — najgorsze bugi nie krzyczą, tylko cicho zawieszają, wylogowują albo drenują budżet.

1. Jeden zły request nie może położyć procesu

Niezłapany throw/rejection w handlerze async potrafi ubić cały serwer (Node/Express: rejection → exit procesu → pętla restartów pm2 — Menedżer, który pilnuje, żeby aplikacja (Node) ciągle działała — restartuje ją po awarii. Bez tego po crashu albo restarcie serwera aplikacja po prostu stoi. pm2 trzyma ją „przy życiu”.). Zbuduj siatkę na poziomie procesu:

  • Globalne łapacze: process.on('unhandledRejection') i 'uncaughtException') — logują i kontrolowanie kończą, nie zostawiają zombie-procesu.
  • Wrapper async-route przekazujący błąd do next(err) zamiast gubić go w niezłapanym promise.
  • Middleware 500, który nie wycieka stack trace użytkownikowi (→ 09).
  • Defensywa na danych z brzegu: konto OAuth bez hasła, pole null tam gdzie kod zakłada string — to realne wejścia, gdy wpuścisz prawdziwych userów (często ujawnia się po swapie bazy, → 05).

Reguła: throw w jednym żądaniu degraduje to żądanie, nie serwis. Otestuj regresyjnie (→ 03).

2. Długie zadania: wznawialne i odpięte od sesji agenta

Scraper/ETL „zbierz wszystko → zapisz raz” traci 100% pracy przy każdym crashu. Pisz batchami:

  • Checkpoint zrobionych jednostek (plik/tabela z URL-ami/ID) — restart wznawia od miejsca, nie od zera.
  • Zapis co N, nie na końcu — crash kosztuje ostatni batch, nie cały run.
  • Długi run odpalaj z realnego terminala, nie z tła sesji Claude. Tło agenta to nietrwały runner — teardown hosta sesji ubija proces w połowie (to nie anti-bot, to znikający runner).
  • Idempotencja — Cecha operacji, którą można uruchomić wiele razy, a wynik jest zawsze taki sam — bez dublowania. Kluczowa przy skryptach i zdarzeniach: ponowne uruchomienie nie psuje danych. Jak włącznik „ON”. całości (→ 04): wznowiony job nie dubluje już zapisanych danych.

3. Zewnętrzne źródła traktuj jak wrogie

Cudze API i strony rwą połączenia, rate-limitują (429), zwracają 200 z HTML-em błędu. Zakładaj zawodność:

  • Timeout na każde wywołanie — bez niego socket wisi w nieskończoność (cichy zawis, nie błąd).
  • Retry z exponential backoff (np. 10/20/30 s), z górnym limitem prób.
  • Rotacja sesji i User-Agent przy masowym I/O — nowe Session (świeże TCP/cookies) co batch, UA z puli realnych przeglądarek (część hostów rwie po ~kilkuset żądaniach z jednej sesji).
  • Waliduj odpowiedź, nie status — „200 + strona błędu” to porażka; sprawdź kształt danych.

4. Infrastruktura providera narzuca limity — weryfikuj end-to-end

Hosting ma własne reguły sieciowe, które kładą „działający” kod dopiero na prodzie:

  • Porty bywają blokowane. Np. wyjściowy SMTP 465 potrafi być zamknięty → użyj 587 + STARTTLS. secure:true na zablokowanym porcie zawiesza każdą wysyłkę aż do timeoutu — ustaw twarde connection/greeting timeouts, żeby błąd był głośny.
  • Deliverability poczty to nie „wysłałem”. Zweryfikowana domena nadawcy (SPF/DKIM), realne wysłanie testowe, opt-in zgodny z RODO / GDPR — Unijne prawo o ochronie danych osobowych — jak wolno zbierać, trzymać i usuwać dane użytkowników. Dotyczy każdej aplikacji z danymi ludzi. Lepiej wpisać zgody i retencję od początku niż płacić kary później. (→ 09). E-mail, który „wyszedł”, a wylądował w spamie/nigdzie, to bug.
  • Sprawdź to na prodzie/stagingu providera, nie lokalnie — lokalna sieć nie ma tych blokad (→ 03).

5. Każdy płatny zasób za twardą kwotą

Endpoint — Pojedynczy „adres” w API, pod który wysyłasz zapytanie po konkretną rzecz (np. listę zamówień). Aplikacje rozmawiają przez endpointy — jeden endpoint = jedna funkcja, którą udostępniasz. wołający płatne API (LLM, geokodowanie, e-mail) bez limitu to otwarty rachunek i wektor nadużyć:

  • Kwota per użytkownik (okno miesięczne/dobowe) z jasnym komunikatem i datą resetu z góry.
  • Różnicuj per tier (free vs premium), egzekwuj po stronie serwera.
  • Rate-limit + nagłówki bezpieczeństwa (helmet/limiter) jako stały element stacku (→ 08).
  • Powiąż z prawem i regulaminem: limit i jego komunikacja to też ochrona przed abuse (→ 09).

6. Wiedz, że prod żyje — observability

Powyższe obrony chronią proda przed śmiercią; observability mówi Ci, że żyjepo zamknięciu okna deployu, zanim user napisze maila. „Weryfikuj, nie deklaruj” (→ 03) zastosowane do działającego systemu:

  • Logi strukturalne z poziomami. error / warn / info (nie print wszędzie). Błędy niosą kontekst (request id, user, co padło) — ale nigdy sekretów ani pełnego PII. Grepujesz logi o 2 w nocy; zrób je grepowalne.
  • Endpoint zdrowia. /healthz zwracający 200 + tani check (baza osiągalna, wersja) — dla load balancera i monitora uptime. Zewnętrzny ping uptime powie Ci o awarii zanim powie klient.
  • Alertuj na to, co boli, nie na szum: skoki 5xx, nieudane płatności/maile, job który nie wystartował, drift selektora / „200 + strona błędu” (→ § 3). Jeden działający alert bije sto dashboardów.
  • Kilka realnych metryk zamiast vanity: error rate, p95 latency, głębokość kolejki, dzienny koszt płatnych API (→ § 5). Mierz, zanim optymalizujesz (→ 13).
  • Błędy → tam, gdzie je zobaczysz (log drain / error tracker), nie tylko stdout, który ucieka.

Zacznij malutko: logi z poziomami + health check + jeden ping uptime łapią większość wartości. Rozwijaj dopiero, gdy realny incydent pokaże lukę (zapisz lekcję w runbooku → 05).

Anty-wzorce

  • 🚫 Brak globalnego łapacza błędów — jeden zły request restartuje serwis dla wszystkich.
  • 🚫 Scraper bez checkpointów odpalany z tła sesji — crash = run od zera, znikający runner = wieczna porażka.
  • 🚫 Zewnętrzne wywołanie bez timeoutu i backoffu — cichy zawis albo ban po serii 429.
  • 🚫 „Wysłałem maila” ≠ dostarczyłem — brak weryfikacji portu/domeny/dostarczalności.
  • 🚫 Płatne API bez kwoty — niespodziewany rachunek i otwarty wektor nadużyć.
  • 🚫 Sesje w pamięci procesu — każdy deploy wylogowuje wszystkich (→ 05).
  • 🚫 Brak logów/alertów — o awarii proda dowiadujesz się od usera, nie od systemu; „działa” bez sposobu, by to wiedzieć.
  • 🚫 Sekrety/PII w logach — log drain staje się wyciekiem.

Dla nowych projektów

Wpisz do Dnia 0 (→ 07) zanim pojawi się pierwszy realny user: globalny error-handler + 500 bez wycieku, timeouty i backoff na każdym zewnętrznym I/O, kwoty na płatnych endpointach, trwały store sesji w osobnym pliku. To tańsze teraz niż jako incydent o 2 w nocy. Każdy incydent prod → wpis w runbooku z datą i ponumerowaną lekcją (→ 05, 06).

Treść doktryny prowadzimy po polsku.