Gdy aplikacja trafia na produkcję: DevOps, integracje i bezpieczna chmura
Klient zapłacił, ale system nadal czeka. Kolejka rośnie, pamięć znika, a powrót do starej wersji nie naprawia bazy. Przechodzimy przez te problemy krok po kroku: od modelu aplikacji i kontraktów integracji po CI/CD, bezpieczeństwo, limity zasobów oraz odtwarzanie po awarii. Z laboratorium .NET, kartami pracy i krótkim quizem.
Poniżej znajduje się pełna treść artykułu. Quizy i symulacje wymagają włączenia JavaScript.
ArcCloud / Warsztat inżyniera / 01
O 9:02 klient zapłacił. O 9:03 aplikacja twierdzi, że nie.
Operator przyjął płatność, ale odpowiedź nie wróciła na czas. Użytkownik odświeża ekran. Aplikacja ponawia żądanie. Chwilę później przychodzą dwa powiadomienia o tej samej transakcji. W logach wszystko wygląda jak kilka niezależnych zdarzeń. Dla klienta to po prostu jedno zamówienie, którego statusu nikt nie potrafi wyjaśnić.
Takie problemy rzadko mieszczą się w jednej metodzie. Przechodzą przez kod, sieć, bazę, kolejkę i sposób wdrażania. Możesz mieć porządnie napisany kontroler, a mimo to dwa razy wystawić dokument. Możesz mieć zielony pipeline, a po publikacji odebrać telefon, że pracownicy nie mogą się zalogować. Możesz uruchomić aplikację w chmurze i nadal nie mieć sposobu na odtworzenie jej danych.
W tym poradniku budujemy sposób myślenia o całym systemie. Każdą ważną decyzję połączymy z objawem, przyczyną, rozwiązaniem i testem, który potrafi wykazać błąd. To właśnie test zostaje z Tobą dłużej niż nazwa kolejnej usługi chmurowej.
Uczciwie o przykładzie. „Pracownia” to fikcyjny system zamówień B2B użyty do nauki. Opisane awarie są rzeczywistymi klasami problemów rozproszonych aplikacji, ale ta historia nie jest relacją z wdrożenia konkretnego klienta. Podane limity i cele są założeniami ćwiczeń, nie gotową konfiguracją dla każdej firmy. Stan dokumentacji sprawdziliśmy 13 października 2026.
Zrozum granice
Kto decyduje o stanie, kto może go zmienić i co stanie się po awarii.
Zobacz błąd
Pobierz małe laboratorium .NET. Duplikaty i przeciążenie przestaną być abstrakcją.
Obroń decyzję
Na końcu czeka osiem pytań z wyjaśnieniami. Bez konta i podawania e-maila.
Czytaj po kolei lub wróć do konkretnego problemu
Mapa drogi do produkcji
01 / Zanim powstaną serwisy
Najpierw ustal, co system obiecuje człowiekowi
Pracownia pozwala firmom zamawiać części. Ma interfejs w Angularze, API w .NET, relacyjną bazę danych i proces działający w tle. Łączy się z operatorem płatności oraz magazynem. Po zatwierdzeniu zamówienia wysyła potwierdzenie i przygotowuje dokument. To wystarczająco dużo, by pojawiły się trudne problemy. Nie potrzebujemy do tego trzydziestu mikroserwisów.
Zaczynamy od rozmowy z osobą, która obsługuje zamówienia. Czy „przyjęte” oznacza zapisane, opłacone czy zarezerwowane w magazynie? Czy wolno zmienić ilość po rozpoczęciu płatności? Co robimy, gdy płatność się udała, a towaru już nie ma? Bez tych odpowiedzi programista nie modeluje procesu. Zapisuje własne domysły w warunkach if.
Oddziel stany, które zmieniają się niezależnie
Jedno pole Status z wartościami „nowe”, „opłacone”, „wysłane”, „błąd” szybko zaczyna kłamać. Zamówienie może być opłacone i jednocześnie czekać na rezerwację towaru. Płatność może wymagać wyjaśnienia, a dokument już istnieć. Modelujemy więc osobno stan zamówienia, płatności i realizacji. Ekran klienta składa te fakty w zrozumiały komunikat: „Płatność potwierdzona. Czekamy na potwierdzenie magazynu”.
Przykład modelu · osobne fakty zamiast jednego „statusu wszystkiego”
Order: Draft → Submitted → Cancelled
Payment: Pending → Confirmed | Failed | NeedsReview
Fulfilment: NotRequested → Reserving → Reserved | Rejected
Reguła: dokument sprzedaży powstaje najwyżej raz
dla konkretnej potwierdzonej transakcji.
Reguła: użytkownik firmy A nie odczytuje zamówień firmy B.
Reguła: timeout nie zmienia automatycznie Pending na Failed.Przejścia wymagają jawnych warunków. Potwierdzenie płatności nie powinno cofać zamówienia do wcześniejszego etapu. Spóźniony komunikat „oczekuje” nie może nadpisać znanego już potwierdzenia. Tam, gdzie dwie osoby zmieniają ten sam rekord, przydaje się kontrola współbieżności: aktualizacja z oczekiwaną wersją i czytelny konflikt, zamiast cichego nadpisania czyjejś pracy.
Wybierz prostotę, którą potrafisz utrzymać
Na początek wystarczą dobrze wydzielone moduły: Zamówienia, Płatności, Realizacja. API obsługuje interakcje, a worker zadania w tle. Moduły mają własne reguły i nie zmieniają sobie danych przez przypadkowe skróty. Rozdzielenie ich na niezależne usługi ma sens, kiedy istnieje konkretny powód: osobny zespół, znacząco inna skala, wymagana izolacja albo niezależny cykl publikacji.
Każdy podział na usługę dodaje sieć, wersjonowanie kontraktu i kolejne miejsce awarii. Zapisz krótką decyzję architektoniczną: problem, rozważone opcje, wybór i warunek powrotu do tematu. „Rozdzielimy generowanie raportów, jeśli zacznie odbierać API zasoby mimo limitów” pomaga bardziej niż diagram, na którym wszystko od razu jest mikroserwisem.
Wybierz jedną operację własnej aplikacji. Zapisz: co użytkownik uważa za sukces, jaki rekord jest źródłem prawdy oraz po jakim zdarzeniu można bezpiecznie pokazać „gotowe”. Jeśli nie potrafisz tego określić, dalsza konfiguracja chmury jeszcze nie rozwiązuje właściwego problemu.
02 / Model na granicy systemów
Integracja zaczyna się od umowy, nie od klienta HTTP
Zewnętrzny magazyn nazywa produkt „artykułem”, ilość zwraca jako tekst, a błąd rezerwacji opisuje kodem, którego nie zna użytkownik. Nie rozprowadzaj tego formatu po domenie. Adapter integracji powinien zamienić obcy kontrakt na pojęcia aplikacji: potwierdzenie rezerwacji, brak towaru, odrzucone dane albo wynik na razie nieznany.
Rozróżniaj polecenie od zdarzenia. ReserveStock to prośba o wykonanie pracy; można ją odrzucić. StockReserved opisuje fakt, który już wystąpił. Gdy nazwy są precyzyjne, łatwiej zdecydować, kto jest właścicielem decyzji i czy wolno ponowić komunikat.
Karta jednej integracji
- Cel: po co wywołujemy partnera i kto odpowiada za jego dostępność?
- Tożsamość: jak aplikacja się uwierzytelnia, jakie ma uprawnienia i jak je odebrać?
- Kontrakt: schemat danych, jednostki, waluta, strefa czasu, wymagane pola, wersja.
- Czas: deadline całej operacji, timeout pojedynczej próby i sposób anulowania.
- Powtórzenia: czy ponowienie jest bezpieczne i przez jaki czas działa klucz idempotencji?
- Awaria: jakie błędy są przejściowe, gdzie trafia odrzucony komunikat i kto do niego wraca?
- Uzgodnienie: jak porównamy nasze rekordy z rekordami partnera po przerwie?
Przykładowy komunikat potrzebuje mniej pól niż rozbudowany obiekt ORM, ale więcej niż samego identyfikatora zamówienia:
JSON · kontrakt zdarzenia, nie eksport całej encji
{
"eventId": "evt-demo-0001",
"type": "OrderSubmitted",
"schemaVersion": 1,
"occurredAt": "2026-10-13T09:00:00Z",
"orderId": "ord-demo-0001",
"tenantId": "tenant-demo-a",
"correlationId": "corr-demo-0001"
}eventId identyfikuje konkretne zdarzenie, orderId obiekt biznesowy, a correlationId pomaga połączyć ślady jednego procesu. Nie są zamienne. Pole tenantId nie nadaje uprawnień: odbiorca musi ufać producentowi i sprawdzić dopuszczalny kontekst. W publicznym API identyfikator firmy pochodzi z autoryzowanej relacji użytkownika, a nie z bezkrytycznie przyjętego JSON-a.
Zmiana schematu też jest wdrożeniem
Najpierw dodawaj opcjonalne pola i ucz odbiorców ignorować pola nieznane. Zmiana znaczenia istniejącego pola jest zmianą łamiącą kontrakt, nawet jeśli JSON nadal się parsuje. Odbiorca nie powinien zamieniać nieznanej waluty w domyślne PLN. Utrzymuj przykładowe komunikaty, testy kontraktu producenta i konsumenta oraz okres współistnienia wersji. Dane pieniężne zapisuj z jawną walutą i ustaloną reprezentacją kwoty, bez przypadkowej arytmetyki zmiennoprzecinkowej.
Nie każda odpowiedź musi przyjść natychmiast. Długie zadanie może zwrócić 202 Accepted i identyfikator operacji, której stan klient później sprawdza. „Przyjęto do wykonania” musi oznaczać coś trwałego. Jeśli zadanie istnieje wyłącznie w pamięci procesu i znika przy restarcie, nie obiecuj użytkownikowi gwarantowanej realizacji.
Scenariusz Pracowni / błąd, którego lokalnie nie widać
„Dodałem retry. Teraz czasem powstają dwie faktury”.
W testach partner zawsze odpowiadał. W naszym przykładzie zapisał dane, ale połączenie zerwało się przed odpowiedzią. Nasz kod powtórzył polecenie. Pierwsza próba nie była nieudana — jej wynik był nieznany. Ta różnica zmienia całą strategię naprawy.
03 / Niepewność jest częścią protokołu
Retry pomaga dopiero wtedy, gdy powtórzenie ma ustalone znaczenie
Idempotencja oznacza tutaj, że wielokrotne wykonanie tej samej logicznej operacji nie tworzy kolejnych skutków biznesowych. Dla utworzenia zamówienia klient zachowuje ten sam klucz przy ponowieniu. Serwer wiąże go z użytkownikiem lub firmą, typem operacji oraz odciskiem istotnych danych. Zmiana ilości przy tym samym kluczu nie może niezauważenie zwrócić wyniku starego zamówienia — powinna dać jednoznaczny konflikt.
Uwierzytelnienie i autoryzację sprawdzaj także przed zwróceniem zapamiętanego wyniku. Stary klucz nie może omijać odebranych uprawnień. W bazie potrzebujesz wymuszenia unikalności oraz transakcji. Sekwencja „sprawdź, czy istnieje; jeśli nie, dodaj” poza taką ochroną przegrywa z dwoma równoległymi żądaniami. Ustal również zachowanie dla operacji w toku, przechowywanie odpowiedzi i okres ważności klucza. Po jego wygaśnięciu ochrona przed ponowieniem może zniknąć. Osobne ograniczenie biznesowe, na przykład unikalny identyfikator transakcji operatora, nadal ma znaczenie.
Szkic transakcji · do dostosowania do konkretnej bazy
BEGIN
claim (TenantId, Operation, IdempotencyKey) -- UNIQUE
sprawdź zgodność odcisku żądania
zapisz Order
zapisz OutboxEvent z trwałym EventId
zapisz wynik operacji powiązany z kluczem
COMMIT
Worker: odczytaj outbox → opublikuj → oznacz wysłanie
Konsument: BEGIN → claim (Consumer, EventId) UNIQUE
→ zmiana biznesowa → COMMIT → ACKOutbox zamyka jedną lukę. Nie usuwa wszystkich
Bez outboxa zapis zamówienia może się udać, a wysłanie komunikatu do kolejki nie. Zapisanie zdarzenia obok zamówienia w tej samej transakcji pozwala workerowi wrócić do niego później. Pozostaje jednak inne okno awarii: broker przyjął wiadomość, ale worker zgasł, zanim oznaczył ją jako wysłaną. Po restarcie opublikuje ją ponownie. Dlatego odbiorca przechowuje identyfikatory obsłużonych zdarzeń, a deduplikację i własną zmianę danych zatwierdza razem.
Duplikat już zatwierdzonego zdarzenia kończysz bez nowego skutku i potwierdzasz odbiór. Konflikt z nadal trwającą operacją wymaga osobnej, ustalonej obsługi. Jeśli skutkiem jest wywołanie kolejnej zewnętrznej usługi, lokalna transakcja nie obejmuje tamtego serwera. Potrzebujesz idempotencji także u partnera, własnego procesu uzgadniania albo jawnej kompensacji. Zwrot pieniędzy nie jest cofnięciem czasu; to nowa operacja, która również może się nie udać. Nie sprzedawaj zespołowi obietnicy „exactly once” bez wskazania, jaki dokładnie zakres gwarancji masz na myśli.
Jedno zdarzenie. Dwie dostawy. Jeden skutek.
Uruchom krótką wizualizację. Kolejne pola opisują tę samą historię również bez animacji.
Pokaż przebieg awarii
Wizualizacja edukacyjna. Niczego nie wysyła do chmury.
Ustal budżet ponowień, zanim ustawisz ich liczbę
Przy odczycie danych partnera można rozważyć ograniczone ponowienia błędów przejściowych, przerwy rosnące z losowym rozrzutem i respektowanie Retry-After. Odpowiedź 400 z błędnym numerem produktu nie naprawi się po sekundzie. Timeout zapisu wymaga innego podejścia niż timeout bezpiecznego odczytu. Każda próba zużywa czas, połączenia i limit partnera.
Załóżmy, że użytkownik może czekać najwyżej osiem sekund. Trzy próby po pięć sekund i dodatkowe przerwy nie mieszczą się w tej obietnicy. Potrzebujesz deadline całego działania i przekazania anulowania do zależności. Nie dokładaj retry niezależnie w trzech warstwach: cztery próby na każdym z trzech poziomów mogą dać do 64 wywołań najgłębszej usługi. Circuit breaker czasowo odcina zawodną zależność; nie zastępuje idempotencji ani limitu jednoczesnych żądań.
Zapisz wynik u partnera, a następnie zasymuluj utratę odpowiedzi. Powtórz żądanie z tym samym kluczem. Oczekuj jednego zamówienia, jednego skutku finansowego i tego samego wyniku. Potem zmień payload przy zachowanym kluczu — oczekuj konfliktu, nie nowego zamówienia.
Dokumentacja: AWS: bezpieczne ponowienia oraz wzorzec transactional outbox.
04 / Publiczne wejście, ograniczone zaufanie
Webhook jest wiadomością od partnera. Najpierw sprawdź, czy naprawdę od niego
Nie potwierdzaj płatności dlatego, że przeglądarka wróciła na adres /success. Nie wystarczy też JSON z polem paid: true. Prawdziwy status opierasz na zweryfikowanym komunikacie operatora lub autoryzowanym odczycie jego API, powiązanym z właściwym zamówieniem i oczekiwaną kwotą oraz walutą.
W schemacie z podpisem HMAC weryfikacja zwykle wymaga dokładnych bajtów ciała żądania. Sparsowanie JSON-a i ponowne jego zapisanie potrafi zmienić spacje lub kolejność pól, a tym samym materiał do podpisu. Korzystaj z oficjalnej biblioteki i specyfikacji konkretnego dostawcy: sposób łączenia znacznika czasu z treścią, kodowanie oraz rotacja sekretów nie są uniwersalne.
Ogranicz wejście. HTTPS, limit wielkości żądania, dopuszczony typ treści, rozsądny czas odczytu. Nie wczytuj dowolnie dużego body tylko po to, żeby odrzucić je później.
Zweryfikuj nadawcę. Podpis na oryginalnych bajtach, właściwy sekret i algorytm. Dla własnej implementacji porównuj podpis w stałym czasie; format sprawdzaj przed porównaniem.
Sprawdź kontekst i powtórzenie. Jeśli protokół to przewiduje: podpisany czas i dopuszczalne okno. Ponadto konto operatora, identyfikator zdarzenia, zamówienie, kwota i waluta. Sam podpis nie zapobiega odtworzeniu starej wiadomości.
Przyjmij trwale. Zapisz zweryfikowane zdarzenie do trwałej skrzynki wejściowej. Sukces HTTP zwróć po trwałym przyjęciu. Powtórny event powinien otrzymać odpowiedź zgodną z kontraktem, bez ponownego skutku.
Przetwarzaj i uzgadniaj. Ciężką pracę wykonaj w tle. Zaplanuj wykrywanie brakujących lub niezgodnych stanów przez porównanie z API partnera.
Okno czasowe chroniące przed odtworzeniem musi uwzględniać sposób podpisywania ponownych dostaw przez konkretnego partnera. Sztywne odrzucanie wszystkich komunikatów starszych niż pięć minut, bez znajomości protokołu, może odciąć prawidłowe opóźnione zdarzenia. Synchronizacja zegara, deduplikacja oraz procedura ręcznego uzgodnienia są częścią rozwiązania.
Testy powinny obejmować podpis poprawny, niepoprawny, brak podpisu, zbyt duże body, dwa równoległe egzemplarze zdarzenia i odwróconą kolejność dostaw. Sprawdź również upadek workera po zapisie skutku, lecz przed potwierdzeniem wiadomości. To często ważniejszy test niż kolejny idealny przykład JSON-a.
Przykład konkretnego protokołu: GitHub: walidacja podpisu webhooka. Parametry swojego operatora zawsze sprawdź w jego dokumentacji.
05 / Architektura z granicami zaufania
Co może być publiczne, a co powinno pozostać poza Internetem?
„Prywatne” oznacza ograniczenie drogi sieciowej. „Chronione” oznacza także kontrolę tożsamości, uprawnień i danych. Te pojęcia nie zastępują się nawzajem. Publiczne API może być poprawnie chronione, a wewnętrzna usługa dostępna dla zbyt wielu kont może być niebezpieczna.
Strona i API
Publiczne materiały, logowanie, kontrolowane operacje klientów i podpisane webhooki. Każde wejście ma własne reguły.
API i worker
Autoryzacja obiektu, reguły biznesowe, limity oraz dostęp wyłącznie do potrzebnych zasobów.
Baza, kolejka, sekrety
Bez bezpośredniego dostępu z przeglądarki. Prywatna łączność tam, gdzie wspiera ją wybrana usługa.
| Zasób | Dostępność | Ochrona, którą sprawdzamy |
|---|---|---|
| Strona, publiczna dokumentacja | Dostępność:Publiczna | Ochrona, którą sprawdzamy:Brak sekretów w JavaScript, HTTPS, bezpieczna polityka treści i aktualne zależności. |
| API zamówień | Dostępność:Publiczna ścieżka dla klientów | Ochrona, którą sprawdzamy:Uwierzytelnianie, autoryzacja każdego obiektu, tenant, walidacja i limity. |
| Webhook operatora | Dostępność:Publiczna, wąska ścieżka | Ochrona, którą sprawdzamy:Podpis, kontrola powtórzeń, ograniczone body i trwałe przyjęcie. |
| Panel administracyjny | Dostępność:Tylko uprawnieni pracownicy | Ochrona, którą sprawdzamy:SSO/MFA, role, rejestr operacji; dodatkowo dostęp sieciowy przez zaufaną bramę, jeśli pasuje do organizacji. |
| Baza, broker, cache | Dostępność:Prywatna sieć lub równoważnie ograniczony dostęp | Ochrona, którą sprawdzamy:Tożsamość aplikacji, minimalne uprawnienia, szyfrowanie, rotacja i brak publicznych portów administracyjnych. |
| Prywatne raporty i załączniki | Dostępność:Niedostępne anonimowo | Ochrona, którą sprawdzamy:Autoryzowany odczyt lub krótko ważny podpisany URL dla jednego zasobu. |
| Metryki, szczegółowe health checks, narzędzia debugowania | Dostępność:Operatorzy i monitoring | Ochrona, którą sprawdzamy:Brak publicznych stack trace, connection stringów, list zasobów i danych klientów. |
Podpisany URL jest poufnym tokenem: jego posiadacz może z niego skorzystać lub przekazać go dalej do czasu wygaśnięcia. Ogranicz jego zakres i czas życia, nie umieszczaj go w publicznych logach. Publiczny kod frontendu jest z definicji do pobrania. Nie ukryjesz w nim klucza API przez minifikację ani zmienną środowiskową dołączoną podczas budowania. Publiczny identyfikator aplikacji czy adres API może być jawny; poświadczenie pozwalające wykonać uprzywilejowaną operację musi pozostać po stronie serwera albo zostać zastąpione odpowiednim mechanizmem delegowania uprawnień.
W Azure samo utworzenie Private Endpoint nie musi wyłączyć istniejącego publicznego dostępu usługi. Weryfikuj ustawienie public network access, reguły zapory oraz DNS. W AWS podobnie sprawdź trasy, security groups, polityki zasobu i endpointów. Test wykonany z wnętrza sieci nie potwierdza, że połączenie z Internetu jest zablokowane.
Z własnego, kontrolowanego środowiska poza siecią aplikacji sprawdź, czy baza i prywatny obiekt odmawiają dostępu. Następnie sprawdź, czy API działa ze swoją tożsamością i czy worker nie może odczytać zasobów należących do innego modułu.
Dokumentacja: Azure Private Endpoint oraz praktyki AWS IAM.
06 / Zabezpieczenie, które działa również poza interfejsem
Ukryty przycisk nie jest kontrolą uprawnień
Klient z firmy A jest zalogowany i może przeglądać własne zamówienia. Zmienia identyfikator w URL na zamówienie firmy B. Jeśli serwer sprawdza jedynie ważność tokena, właśnie udostępnił cudze dane. Każda operacja na obiekcie wymaga sprawdzenia relacji użytkownik–firma–zasób–czynność. Odpowiedź „nie znaleziono” może ograniczyć ujawnianie istnienia obiektu, lecz najważniejsze jest odrzucenie dostępu przed zwróceniem danych.
Pseudokod zapytania · tenant ustala zaufany kontekst
tenant = authorizedMembership.For(currentUser)
order = Orders.SingleOrDefault(
id == requestedId AND tenantId == tenant.Id)
if order is null: return NotFound
if !policy.CanCancel(currentUser, order): return Forbidden
order.Cancel(serverTime)Token nie jest prawdziwy tylko dlatego, że da się odczytać jego JSON. Korzystaj ze sprawdzonego middleware, weryfikuj podpis, wystawcę, odbiorcę i czas ważności. Role uprzywilejowane wymagają osobnej polityki i MFA. Zmiana uprawnień oraz zakończenie sesji powinny mieć przewidziane zachowanie, zwłaszcza jeśli token pozostaje ważny przez dłuższy czas.
Pięć miejsc, w których zabezpieczenia często się rozchodzą
- Sesja w przeglądarce. Dobierz cookies i tokeny do architektury. Dla sesji cookie ustaw m.in. Secure i HttpOnly, świadomie wybierz SameSite i ochronę CSRF. HttpOnly ogranicza odczyt cookie przez skrypt, ale samo nie rozwiązuje XSS ani CSRF.
- CORS. Kontroluje zachowanie przeglądarki przy odczycie odpowiedzi między originami. Nie zastępuje uwierzytelniania i nie zatrzyma dowolnego klienta HTTP.
- Wejście użytkownika. Parametryzuj zapytania SQL, koduj dane w kontekście wyjścia i unikaj wstrzykiwania niezaufanego HTML. Waliduj długość, zakres i dozwolone wartości na serwerze.
- Załączniki. Sprawdzaj rozmiar i faktyczny typ, generuj własne nazwy, odseparuj pliki od kodu aplikacji. Archiwa mogą rozpakować się do ogromnych rozmiarów. Skanowanie i kwarantanna nie powinny otwierać bezpośredniej drogi do publicznego katalogu.
- Pobieranie obcego URL. Funkcja importu lub audytu może zostać użyta do SSRF. Ogranicz protokoły i cele, blokuj adresy wewnętrzne i metadata, sprawdzaj także przekierowania oraz wynik rozwiązywania DNS. Dołóż kontrolę wyjścia na poziomie sieci.
Limity żądań muszą uwzględniać koszt. Sto prostych odczytów i sto generowanych raportów to inne obciążenie. Ograniczaj kosztowne operacje według uwierzytelnionej tożsamości i firmy, a publiczne wejścia także według sygnałów sieciowych. Zwracaj zrozumiałą informację o przekroczeniu limitu; nie ujawniaj przy tym szczegółów infrastruktury.
WAF, skaner zależności i test penetracyjny są użyteczne, ale nie przenoszą odpowiedzialności za reguły dostępu z aplikacji na narzędzie. W planie testów umieść konto z innej firmy, konto bez roli, wygasłą sesję i żądanie wysłane bez korzystania z interfejsu. Nie da się uczciwie obiecać systemu „odpornego na każde włamanie”. Można konsekwentnie ograniczać powierzchnię ataku, wykrywać nadużycia i ćwiczyć odebranie dostępu.
Do checklisty testów: OWASP: autoryzacja na poziomie obiektu oraz ochrona przed SSRF.
07 / Powtarzalny proces, nie skrypt bohatera
Dobry DevOps zaczyna się od możliwości odtworzenia wdrożenia
W zespole jest osoba, która „wie, jak to wrzucić”. Na jej komputerze leży plik z konfiguracją, a na serwerze kilka zmian wykonanych ręcznie. Dopóki jest dostępna, wszystko wygląda sprawnie. Problem pojawia się przy urlopie, incydencie albo drugim środowisku. Wiedza operacyjna nie może istnieć wyłącznie w czyjejś pamięci i historii terminala.
Repozytorium powinno opisywać kod, definicję budowania, infrastrukturę i niesekretną konfigurację. Sekrety przechowuje właściwy magazyn, a dokumentacja wskazuje ich nazwy, właściciela i sposób rotacji. Środowiska testowe oraz produkcyjne mają oddzielne tożsamości i zakresy dostępu. „Inna baza na tym samym koncie administratora” nie daje wystarczającej izolacji.
Reguły domeny, integracje, kontrakty i skany.
Wersja, commit, digest i zależności.
Migracja, smoke test, uprawnienia i limity.
Ten sam artefakt, obserwacja, decyzja o kontynuacji.
Buduj raz, promuj ten sam wynik
Jeżeli staging i produkcja budują aplikację osobno, testowałeś podobny produkt, niekoniecznie ten sam. Zablokuj rozwiązywanie zależności plikiem lock, zapisuj wersję toolchainu i przenoś między środowiskami ten sam obraz identyfikowany digestem. Tag latest nie wystarczy do odpowiedzi na pytanie, jaki kod działał we wtorek o 14:10.
Przydatne ślady to commit, identyfikator uruchomienia pipeline, suma artefaktu, wynik testów, wykaz zależności i informacja o zatwierdzeniu publikacji. SBOM pomaga ustalić, czy wykryta podatność dotyczy Twojego obrazu. Podpis i poświadczenie pochodzenia pomagają sprawdzić, skąd obraz pochodzi. Żaden z tych dokumentów sam nie oznacza, że kod jest bezpieczny.
CI/CD nie powinno przechowywać wiecznego klucza administratora
Federacja OIDC pozwala wymieniać tożsamość zadania na krótkotrwałe uprawnienia w chmurze. Ochrona zależy jednak od warunków zaufania: właściwego wystawcy, odbiorcy tokena, repozytorium, gałęzi lub chronionego środowiska. Sam zapis id-token: write w GitHub Actions daje możliwość uzyskania tokena OIDC, nie nieograniczony dostęp do AWS czy Azure.
Sprawdź rzeczywisty format claimu sub dla repozytorium i dostawcy. GitHub zmienia sposób identyfikowania nowych repozytoriów; przykładowa polityka skopiowana ze starego wpisu może nie pasować. Po stronie chmury rola ma pozwalać na publikację konkretnych zasobów, a nie na zarządzanie całym kontem. Job testujący obcy pull request nie powinien otrzymywać poświadczeń produkcyjnych.
Najpierw zaufanie, potem wygoda. Zablokuj uruchomienie publikacji z nieuprawnionej gałęzi, chroń środowisko produkcyjne i nie interpoluj niezaufanych tytułów PR bezpośrednio w poleceniach shella. Zewnętrzne akcje przypinaj do zweryfikowanych, niezmiennych rewizji i aktualizuj je kontrolowanym procesem.
Zielony pipeline musi coś udowadniać
Test jednostkowy sprawdza regułę; test integracyjny powinien uruchomić prawdziwy silnik bazy i wykazać działanie transakcji oraz ograniczeń. Test kontraktu chroni granicę z partnerem. Test E2E sprawdza najważniejszą ścieżkę użytkownika. Dodaj celowe błędy: odmowę dostępu, timeout, duplikat i niezgodny schemat. Sto testów opartych na idealnym mocku nie potwierdza poprawnej izolacji transakcji.
Osobno określ, co zatrzymuje publikację. Błąd autoryzacji lub testu migracji powinien blokować wdrożenie. Skan podatności potrzebuje zasad oceny istotności, czasu naprawy i obsługi wyjątków. Zespół, który ignoruje stale czerwony skaner, ma tylko dekorację w pipeline.
Dokumentacja: GitHub Actions i OIDC oraz bezpieczne używanie workflow.
08 / Stara i nowa wersja żyją obok siebie
Rollback aplikacji nie cofa automatycznie zmiany danych
Nowa wersja zapisuje inne znaczenie pola, migracja usuwa starą kolumnę, a kilka minut później rośnie liczba błędów. Przywracasz poprzedni obraz. On nadal oczekuje starej struktury. Przycisk „rollback” działa technicznie, lecz aplikacja nie działa biznesowo. Bezpieczeństwo powrotu planuje się przed publikacją.
Rozszerz, przenieś, dopiero potem usuń
W podejściu expand–contract pierwsze wdrożenie dodaje nową strukturę w sposób zgodny ze starą wersją. Kolejny etap przenosi dane porcjami i utrzymuje zgodność zapisów. Potem aplikacja zaczyna korzystać z nowego modelu. Dopiero po sprawdzeniu, że stare procesy i odbiorcy już go nie potrzebują, osobna zmiana usuwa przestarzały element.
Przykład · zmiana jednej kolumny adresu na strukturę
1. Dodaj nowe pola; stara wersja nadal czyta stare.
2. Wdróż zgodny zapis starego i nowego formatu.
3. Uzupełnij historyczne dane małymi, wznawialnymi partiami.
4. Porównaj kompletność; przełącz odczyt pod kontrolą flagi.
5. Obserwuj, zachowując możliwość powrotu odczytu.
6. Usuń stary format w osobnym, późniejszym wdrożeniu.Przed przełączeniem sprawdź wszystkie aktywne ścieżki zapisu, także stare instancje oraz workery. Nie mogą niezauważenie utrzymywać wyłącznie starego formatu. Backfill powinien sprawdzać wersję rekordu lub odpowiedni warunek aktualizacji, żeby nie nadpisać świeższej zmiany. Podwójny zapis też wymaga poprawności. Jeśli oba pola są w jednej bazie, użyj jednej transakcji. Jeśli w dwóch usługach, potrzebujesz procesu synchronizacji i uzgadniania rozbieżności. Backfill ma dać się bezpiecznie ponowić; długie blokowanie tabeli podczas godzin pracy potrafi być większym problemem niż sama zmiana schematu.
Wdrożenie stopniowe ma z góry określony warunek przerwania
Nowa wersja może dostać niewielką część ruchu, zanim obsłuży wszystkich. Obserwuj błędy, opóźnienie i wynik biznesowy, a nie tylko to, czy proces nie zgasł. Ustal próg, okno pomiaru i osobę podejmującą decyzję. Flaga funkcji pozwala wyłączyć nową ścieżkę, ale nie odwróci już wykonanej operacji finansowej ani nie zastąpi kompatybilności danych.
Po publikacji wykonaj krótkie testy: zalogowanie konta testowego, autoryzowany odczyt, kontrolowaną operację oraz sprawdzenie komunikatu przez worker. Środowisko i dane do tych prób muszą być rozpoznawalne, żeby monitoring nie pomylił ich z ruchem klientów. Automatyczny test produkcyjny nie powinien przypadkiem obciążać prawdziwej karty ani wysyłać wiadomości do losowych odbiorców.
Dlaczego kompatybilność jest częścią rollbacku: AWS Builders’ Library.
Scenariusz Pracowni / dyżur o 14:17
„Dorzuciliśmy RAM. Problem wrócił godzinę później”.
Importer odczytuje cały plik, buduje listę wszystkich rekordów i uruchamia zadanie dla każdego z nich. Większa maszyna tylko odsunęła awarię. Potrzebujemy wiedzieć, ile pracy może być w toku, jak duży jest pojedynczy element i co zrobimy, gdy producent będzie szybszy od konsumenta.
09 / Zasoby są częścią kontraktu
Pamięć ograniczamy na kilku poziomach, nie jedną zmienną
Najpierw zmierz. Interesują nas pamięć całego procesu lub kontenera, sterta zarządzana przez GC, tempo alokacji, przerwy GC i liczba operacji w toku. Rosnąca sterta po kolejnych pełnych zbiórkach sugeruje coś innego niż stabilna sterta i rosnąca pamięć natywna. Jednorazowy skok przy generowaniu dużego pliku wymaga innej poprawki niż bezterminowo rosnący cache.
Typowe przyczyny są prozaiczne: ToListAsync() na całej tabeli, ogromny JSON w pamięci, słownik bez wygaszania, brak zwalniania strumieni, nadmierna liczba zadań równoległych lub logowanie całych dokumentów. Ustal maksymalny rozmiar strony, pliku, żądania i wyniku. Przetwarzaj strumieniowo tam, gdzie pozwala na to format; czytaj małe partie i nie przechowuj niepotrzebnie ich kopii.
Ograniczona kolejka to świadoma odpowiedź na przeciążenie
Jeśli napływa 200 zadań na sekundę, a wykonujesz 50, zaległość rośnie o około 150 co sekundę. Bez limitu pamięć tylko magazynuje problem. Możesz czekać na wolne miejsce, odrzucić pracę z komunikatem o przeciążeniu lub trwale przekazać ją do brokera. Wybór zależy od umowy z użytkownikiem. Ciche wyrzucanie zadania po pokazaniu „przyjęto” nie jest poprawną strategią.
C# · kanał z limitem liczby elementów
var queue = Channel.CreateBounded<Job>(
new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.Wait,
SingleReader = true,
SingleWriter = false
});
// Czeka na miejsce; uwzględnia anulowanie.
await queue.Writer.WriteAsync(job, cancellationToken);
// Alternatywa: odmowa bez czekania, jeśli kanał jest pełny.
bool accepted = queue.Writer.TryWrite(job);Nieograniczona liczba producentów czekających w WriteAsync nadal trzyma zadania i bufory w pamięci. Ogranicz również liczbę równoczesnych żądań lub producentów oraz czas oczekiwania. Te dwie ostatnie linie pokazują alternatywne sposoby dodawania pracy — nie wykonuj obu dla tego samego zadania. Kanał istnieje w pamięci procesu i nie jest trwałym brokerem. Pojemność 100 ogranicza liczbę elementów, a nie ich łączny rozmiar. Sto obiektów z plikiem po 20 MB nadal zajmuje bardzo dużo pamięci. Lepiej przekazać identyfikator pliku w prywatnym storage i pobierać go kontrolowanym strumieniem.
Request w Kubernetes nie jest sufitem
requests.memory służy między innymi planowaniu umieszczenia poda na węźle. limits.memory wyznacza ograniczenie kontenera egzekwowane przez mechanizmy systemu. Proces może zostać zakończony z powodu braku pamięci; aplikacja nie zawsze dostanie szansę na elegancką obsługę wyjątku. Limit CPU może powodować ograniczanie czasu procesora, więc zbyt ciasne ustawienie potrafi zwiększyć opóźnienie i liczbę timeoutów.
Fragment manifestu · wartości wyłącznie do pomiaru w laboratorium
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"Ten fragment nie opisuje kompletnego wdrożenia. Obejmuje pojedynczy kontener. Przy kilku replikach, sidecarach i zasobach węzła całkowite zużycie jest inne. Dobieraj wartości do wyników testu obciążeniowego, czasu startu i profilu GC; zostaw zapas, a następnie obserwuj, czy system go potrzebuje.
Pułapka .NET: „60” nie zawsze znaczy sześćdziesiąt
Limit sterty GC nie jest limitem całej pamięci procesu. Poza nim pozostają między innymi stosy wątków, część struktur runtime i alokacje natywne. Ustawienie sterty na cały limit kontenera może zostawić zbyt mało miejsca na resztę. Najpierw sprawdź zachowanie runtime w kontenerze; domyślne mechanizmy uwzględniają ograniczenia środowiska, a ręczna konfiguracja powinna wynikać z pomiaru.
Dwa alternatywne sposoby zapisania 60% · przykład, nie zalecenie dla każdego API
# Zmienna środowiskowa: zapis szesnastkowy
DOTNET_GCHeapHardLimitPercent=0x3C
// Fragment runtimeconfig.json: liczba dziesiętna
"System.GC.HeapHardLimitPercent": 60W zmiennej środowiskowej wartość 60 jest interpretowana jako szesnastkowe 0x60, czyli 96 dziesiętnie. Sprawdź też pierwszeństwo ustawień: jawny limit w bajtach może sprawić, że procent nie zostanie użyty. Nie ustawiaj wszystkich wariantów naraz „na wszelki wypadek”.
Uruchom import tego samego pliku przy równoległości 1, 4 i 16. Zapisz szczyt pamięci, czas wykonania, błędy i obciążenie partnera. Wybierz poziom, który spełnia cel i zostawia zapas. Sama najkrótsza długość testu nie oznacza najlepszej konfiguracji dla reszty aplikacji.
Szczegóły: konfiguracja GC w .NET, requests i limits w Kubernetes oraz System.Threading.Channels.
10 / Usługa ma koszt i odpowiedzialność
AWS i Azure: wybieraj potrzebne możliwości, nie największy diagram
Dla małego zespołu zarządzane środowisko aplikacyjne może być rozsądniejszym początkiem niż własny klaster Kubernetes. Klaster daje kontrolę, lecz dokłada aktualizacje, sieć, polityki, pojemność i dyżur. Wybierz go wtedy, gdy zespół potrzebuje tych możliwości i potrafi je utrzymać. Sam fakt, że aplikacja jest w kontenerze, nie wymaga od razu orkiestratora o największej liczbie opcji.
Poniższa mapa pokazuje przykładowe usługi o pokrewnym zastosowaniu. Nie są zamienne jeden do jednego. Różnią się semantyką, limitami, rozliczaniem, regionami i zakresem odpowiedzialności:
| Potrzeba | Przykład AWS | Przykład Azure | Przed wyborem |
|---|---|---|---|
| Uruchomienie kontenera | Przykład AWS:ECS z Fargate | Przykład Azure:Container Apps | Przed wyborem:Skalowanie, sieć, zadania w tle, limity czasu i diagnostyka. |
| Relacyjna baza PostgreSQL | Przykład AWS:RDS for PostgreSQL | Przykład Azure:Azure Database for PostgreSQL | Przed wyborem:Połączenia, HA, backup, odtwarzanie i wersje rozszerzeń. |
| Kolejka pracy | Przykład AWS:SQS | Przykład Azure:Service Bus | Przed wyborem:Kolejność, duplikaty, blokada wiadomości, maksymalny payload i DLQ. |
| Pliki | Przykład AWS:S3 | Przykład Azure:Blob Storage | Przed wyborem:Prywatność, podpisane URL, retencja, lifecycle i koszt transferu. |
| Tożsamość aplikacji i sekrety | Przykład AWS:IAM roles, Secrets Manager | Przykład Azure:Managed Identity, Key Vault | Przed wyborem:Zakres roli, zaufanie, rotacja i ślad użycia. |
| Monitoring | Przykład AWS:CloudWatch | Przykład Azure:Azure Monitor | Przed wyborem:Retencja, eksport, redakcja danych oraz koszt liczby zdarzeń. |
Granice domeny i kontrakty integracji są bardziej przenośne niż nazwy usług. Adapter może ukryć szczegóły brokera, ale nie powinien obiecywać gwarancji, których jedna z implementacji nie daje. Jeśli porządek wiadomości jest wymaganiem biznesowym, zapisz zakres tej kolejności i sprawdź go dla wybranej konfiguracji.
Alert budżetowy nie zatrzymuje licznika
Budżety pomagają wykryć wzrost kosztu, ale dane rozliczeniowe przychodzą z opóźnieniem. Nie traktuj alertu na 100 zł jako gwarancji, że rachunek nigdy tej wartości nie przekroczy. Automatyczne akcje też mają opóźnienia i ograniczony zakres. Zatrzymanie bazy może przerwać pracę firmy, a część kosztów nadal pozostanie.
Koszt ograniczasz wcześniej: maksymalną liczbą replik, limitem równoległych zadań, rozmiarem uploadu, czasem działania zadania, limitem żądań i polityką retencji. Dodaj właściciela oraz tagi kosztowe do zasobów. Sprawdzaj zapomniane środowiska testowe, stare snapshoty, transfer między regionami, NAT i duplikowane logi. Autoskalowanie bez określonego maksimum potrafi zmienić błąd w kodzie w problem finansowy.
Ustal także zachowanie po osiągnięciu limitu: odrzucenie nowego zadania, kolejka z przewidywanym czasem albo ograniczenie wybranej funkcji. Nigdy nie opieraj ochrony konta chmurowego wyłącznie na liczniku w przeglądarce. Ostateczna kontrola jest po stronie serwera i platformy.
Granice mechanizmu budżetów: Azure Cost Management i AWS Budgets.
11 / Mierz usługę z perspektywy człowieka
Serwer odpowiada 200. Dlaczego klient nadal nie ma raportu?
API może działać poprawnie, gdy worker od godziny nie odbiera zadań. Dlatego obok liczby błędów HTTP potrzebujesz informacji o wieku najstarszej wiadomości, liczbie operacji w stanie „oczekuje” i czasie zakończenia procesu. Średnia długość kolejki nie mówi, czy jeden klient utknął na zawsze.
W Pracowni przyjmijmy ćwiczebny cel: 99% poprawnie przyjętych zamówień ma dostać potwierdzenie magazynu w ciągu dwóch minut. To SLO dla konkretnej funkcji, nie ogólna obietnica całej aplikacji. Musisz zdefiniować początek i koniec pomiaru, wyłączenia oraz to, jak uwzględniasz awarię partnera. Pomiar ma odzwierciedlać doświadczenie klienta, nawet jeśli bezpośrednia przyczyna leży poza Twoim serwerem.
Czy jest problem?
Odsetek błędów, p95/p99, wiek zadania, zaległość, pamięć, koszt.
Co się wydarzyło?
Uporządkowane zdarzenia, wynik operacji, identyfikator próby i wersja aplikacji.
Gdzie uciekł czas?
Powiązanie API, bazy, brokera i wywołania partnera w jednym przepływie.
Przenoś kontekst śledzenia przez HTTP i komunikaty, używając standardowych mechanizmów, na przykład OpenTelemetry. Do logów dodaj identyfikator zdarzenia i operacji. Nie zapisuj tokenów, sekretów, całych dokumentów ani niepotrzebnych danych osobowych. Telemetria ma określoną retencję i dostęp. Nie umieszczaj orderId jako etykiety każdej metryki: ogromna liczba unikalnych wartości może zwiększyć koszt i obciążyć system monitoringu.
Trzy sondy, trzy różne pytania
- Startup: czy aplikacja zdążyła się uruchomić? Daje czas wolniej startującym procesom.
- Readiness: czy ta instancja może teraz przyjmować ruch? Niepowodzenie usuwa ją z gotowych odbiorców ruchu, zamiast automatycznie restartować.
- Liveness: czy proces utknął tak, że restart może pomóc? Niepowodzenie po ustalonym progu może wywołać restart kontenera.
Jeżeli liveness bezwarunkowo zależy od bazy, krótka awaria bazy może uruchomić lawinę restartów wszystkich aplikacji. Readiness także trzeba projektować świadomie: jeśli wszystkie instancje zależą od tego samego partnera, odcięcie ich wszystkich może pogorszyć sytuację. Czasem lepiej wyłączyć jedną funkcję i zachować dostęp do pozostałych.
Dobry alert mówi, kto ma zareagować, jaki jest wpływ i gdzie zajrzeć najpierw. „CPU 85%” bez kontekstu nie zawsze wymaga pobudki. „Przez pięć minut nie zakończono żadnego poprawnego zadania, a najstarsze czeka 12 minut” podpowiada konkretną diagnozę. Dołącz do alertu krótki runbook: dashboard, pierwsze trzy sprawdzenia, bezpieczna akcja i sposób eskalacji.
Dokumentacja: znaczenie sond w Kubernetes oraz OpenTelemetry w .NET.
12 / Spokój buduje się przed alarmem
Plan awarii ma działać również wtedy, gdy autor systemu śpi
Wróćmy do płatności z początku artykułu. Widzimy status „oczekuje”, a klient twierdzi, że zapłacił. Najgorsza pierwsza reakcja to ręczne ponowienie obciążenia. Najpierw zatrzymaj automatyczne tworzenie kolejnych skutków, ustal identyfikatory transakcji i porównaj stan operatora z naszym zapisem. Dopiero na tej podstawie wznowisz przetwarzanie, oznaczysz sprawę do wyjaśnienia albo uruchomisz uzgodnioną kompensację.
Runbook: opóźnione potwierdzenia płatności
- Ustal zakres: pojedyncze zamówienie, jeden partner czy wszystkie firmy? Zapisz czas i wersję wdrożenia.
- Sprawdź zdrowie odbiornika webhooków, podpisy, błędy trwałego zapisu i wiek wiadomości.
- Porównaj identyfikator transakcji, kwotę i walutę przez autoryzowane API operatora.
- Wstrzymaj niebezpieczne powtórzenia. Zachowaj komunikaty i dane potrzebne do diagnozy.
- Napraw przyczynę, następnie odtwórz wybrane wiadomości z zachowaniem ich identyfikatorów i deduplikacji.
- Sprawdź pojedynczy efekt biznesowy, stan klienta i malejący wiek zaległości. Poinformuj obsługę o wyniku.
DLQ nie jest koszem, którego nikt nie otwiera
Dead-letter queue przechowuje wiadomości, których nie udało się obsłużyć zgodnie z zasadami. Każda powinna mieć rozpoznawalną przyczynę, liczbę prób i właściciela. Po poprawce nie przesyłaj bezmyślnie całej kolejki z maksymalną szybkością: możesz powtórzyć awarię albo przeciążyć partnera. Zacznij od małej próbki, monitoruj wynik i dopiero zwiększaj tempo.
Backup to początek dowodu, nie jego koniec
Ustal RPO, czyli akceptowalną utratę danych wyrażoną w czasie, oraz RTO, czyli docelowy czas przywrócenia usługi. Ćwiczebne RPO 15 minut i RTO dwie godziny są wymaganiami do sprawdzenia, nie właściwością kupionej maszyny. Backup raz na dobę nie spełni pierwszego celu bez dodatkowych mechanizmów.
Odtwórz kopię w odizolowanym środowisku. Sprawdź klucze szyfrujące, uprawnienia, kompletność plików i zgodność wersji aplikacji. Zweryfikuj reguły biznesowe, liczbę rekordów i możliwość odczytu dokumentów. Wyłącz wysyłkę e-maili i rzeczywiste płatności w środowisku odtwarzania, aby test nie powtórzył operacji klientów.
Po cofnięciu bazy jej stan może być starszy niż zdarzenia już wysłane do partnera. Potrzebujesz uzgodnienia danych po restore. Ochrona kopii przed usunięciem i odseparowanie uprawnień backupu od zwykłego konta aplikacji zmniejszają ryzyko, że jeden przejęty dostęp zniszczy produkcję oraz ostatnią możliwość powrotu.
Po incydencie zapisz oś czasu, wpływ, przyczynę i konkretne zadania z właścicielem. „Uważać następnym razem” nie jest poprawką systemu. Test duplikatu, zmiana limitu, dodatkowy sygnał albo łatwiejszy rollback już nią są. Dobra analiza pomaga kolejnej osobie; nie służy do szukania winnego.
Weryfikacja kopii: AWS Well-Architected: okresowe testy odzyskiwania danych.
13 / Przeczytane? Teraz sprawdź własnymi rękami
Laboratorium .NET: zepsuj przepływ, zanim zrobi to sieć
Pobierz niewielki projekt konsolowy z kodem źródłowym i polską instrukcją. Działa lokalnie, bez konta chmurowego, kluczy i zewnętrznych pakietów NuGet. Przechodzi przez osiem samosprawdzających się scenariuszy: ponowienie, konflikt payloadu, wyścig 32 żądań, zakres klucza firmy, nieudane zatwierdzenie, awarię publikacji, deduplikację konsumenta i pełną kolejkę.
Po rozpakowaniu · w katalogu sample
dotnet run --project ArcCloud.ProductionPatterns.Lab.csproj --configuration ReleaseOczekiwany wynik to osiem komunikatów PASS i kod zakończenia 0. Projekt celuje w net8.0 dla zgodności laboratorium i wymaga odpowiednich pakietów referencyjnych SDK. README opisuje uruchamianie na nowszym runtime. Dla nowego produktu oceniaj aktualnie wspieraną wersję .NET 10 LTS; cel tego małego ćwiczenia nie jest rekomendacją wersji dla nowego systemu.
Co sprawdziliśmy: projekt skompilował się i zaliczył 8/8 scenariuszy lokalnie na SDK 9.0.200. To dowód działania przykładu w opisanym środowisku, nie test Azure, AWS ani Twojej bazy. Magazyn jest nietrwały i działa w jednym procesie; produkcja potrzebuje transakcji, trwałości i testów między replikami.
Trzy rundy, które pomagają zrozumieć mechanizm
- Przewidź wynik. Przed uruchomieniem scenariusza
DispatcherCrashzapisz, ile będzie zamówień, dostaw i skutków. Dopiero potem przeczytaj wynik. - Wprowadź kontrolowany błąd w kopii. Pomiń sprawdzenie inbox. Test duplikatu powinien wykryć dodatkowy skutek. Jeśli nadal przechodzi, sprawdź, czy zmieniłeś właściwy fragment i czy test mierzy właściwą rzecz.
- Przenieś zasadę do własnej aplikacji. Znajdź operację, która może się powtórzyć. Zapisz jej klucz, zakres, czas przechowywania, granicę transakcji i sposób uzgodnienia wyniku.
W paczce są także karty: kontrakt integracji, plan wdrożenia i runbook incydentu. Wypełnij je rzeczywistymi nazwami, właścicielami oraz progami. Puste „monitoring: jest” zastąp adresem konkretnego dashboardu i warunkiem, po którym ktoś podejmuje działanie.
14 / „Gotowe” musi mieć dowód
Jak odebrać aplikację, żeby lista zadań nie udawała jakości?
Odbiór warto zorganizować wokół scenariuszy, a nie liczby ukończonych ticketów. Każda obietnica potrzebuje warunku wejściowego, oczekiwanego wyniku i miejsca, w którym wynik zapisano. Wtedy nowa osoba może odtworzyć sprawdzenie, zamiast przyjmować na wiarę, że ktoś „już to klikał”.
| Próba | Oczekiwany wynik | Dowód |
|---|---|---|
| Dwa równoczesne żądania z tym samym kluczem | Oczekiwany wynik:Jedno zamówienie i zgodny wynik obu prób | Dowód:Test na docelowym silniku bazy oraz kontrola rekordów. |
| Przerwanie workera po publikacji | Oczekiwany wynik:Możliwa podwójna dostawa, pojedynczy skutek | Dowód:Identyfikatory wiadomości, inbox i końcowy stan. |
| Odczyt zasobu innej firmy | Oczekiwany wynik:Brak ujawnionych danych | Dowód:Test autoryzacji poza interfejsem użytkownika. |
| Zmieniony podpis lub nadmiarowy payload | Oczekiwany wynik:Odrzucenie przed skutkiem biznesowym | Dowód:Test webhooka, liczba zapisów, bezpieczny log. |
| Długie obciążenie i nagły skok ruchu | Oczekiwany wynik:Znane limity, kontrolowana odmowa, odzyskanie sprawności | Dowód:Profil pamięci, opóźnienia, błędy i wiek kolejki. |
| Powrót do poprzedniej wersji | Oczekiwany wynik:Zgodność z aktualnym schematem danych | Dowód:Zapis próby wdrożenia i scenariusza biznesowego. |
| Odtworzenie danych z kopii | Oczekiwany wynik:Potwierdzone RPO/RTO i poprawne dane | Dowód:Czas pomiaru, test odczytu, lista uzgodnionych rozbieżności. |
| Alarm i odebranie uprawnień | Oczekiwany wynik:Właściwa osoba dostaje sygnał; odebrany dostęp przestaje działać | Dowód:Kontrolowane ćwiczenie operacyjne i ślad zdarzeń. |
Do odbioru dołącz widok użytkownika: czy komunikat tłumaczy opóźnienie, czy można wrócić do operacji po odświeżeniu i czy stan jest dostępny dla obsługi. Bez tego technicznie poprawne „202” może być dla klienta pustą stroną. Wydajność, dostępność i użyteczność spotykają się właśnie w takich momentach.
Jak zachować tę wiedzę na dłużej
Nie musisz zapamiętać całego tekstu. Warto zapamiętać pytania, które wracają w każdym projekcie: „co, jeśli odpowiedź nie dotrze?”, „kto może zmienić ten rekord?”, „co ogranicza ilość pracy?”, „jak odtworzymy dane?” i „jaki test może wykazać, że się mylimy?”.
Uruchom laboratorium i własnymi słowami wyjaśnij różnicę między jedną operacją a dwiema dostawami.
Rozwiąż quiz bez zaglądania do rozdziałów. Wróć tylko do odpowiedzi, których nie potrafisz uzasadnić.
Wypełnij kartę jednej prawdziwej integracji. Dodaj do projektu test awarii, którego dotąd brakowało.
Przećwicz rollback lub restore w bezpiecznym środowisku. Zapisz, co rzeczywiście udało się odtworzyć.
Jeden artykuł nie zastąpi praktyki, przeglądu kodu ani odpowiedzialności za działającą usługę. Może jednak dać Ci powtarzalny warsztat: założenie, implementacja, próba awarii, pomiar i korekta. Ten proces zostaje użyteczny także wtedy, gdy zmienią się nazwy bibliotek.
15 / Osiem decyzji, które warto umieć uzasadnić
Krótki test: co zrobisz na dyżurze?
Wybierz jedną odpowiedź w każdym pytaniu. Od razu poznasz jej konsekwencję i możesz ją poprawić. To ćwiczenie do nauki, nie certyfikat. Wynik nie jest wysyłany na serwer ani zapisywany po odświeżeniu.
Decyzja 1 / 8
Operator mógł wykonać zapis, ale dostaliśmy timeout. Co dalej?
Ponawiam z nowym kluczem, żeby na pewno utworzyć operację.
Uzgadniam wynik; bezpieczne ponowienie zachowuje ten sam klucz i dane.
Od razu oznaczam płatność jako odrzuconą.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Decyzja 2 / 8
Outbox opublikował wiadomość, ale worker zgasł przed oznaczeniem jej jako wysłanej.
Po restarcie wiadomość może przyjść drugi raz; inbox i skutek zatwierdzam razem.
Outbox gwarantuje, że duplikat nigdy nie wystąpi.
Usuwam nieoznaczone rekordy, żeby nie wysłać ich ponownie.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Decyzja 3 / 8
Zalogowany klient zmienia ID zamówienia na ID innej firmy.
Pozwalam, skoro token jest ważny.
Ukrywam w interfejsie pole z ID.
Sprawdzam firmę, zasób i uprawnienie do tej czynności przed zwróceniem danych.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Decyzja 4 / 8
Kanał mieści 100 elementów. Każdy może zawierać ogromny plik.
Pamięć jest bezpieczna, bo kolejka ma stałą długość.
Ograniczam też payload, równoległość i bufory; do kolejki mogę przekazać ID pliku.
Zwiększam kolejkę do 10 000 elementów.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Decyzja 5 / 8
Chcę wpisać 60% w zmiennej DOTNET_GCHeapHardLimitPercent.
Wpisuję 0x3C i pamiętam, że to limit sterty GC, nie całego procesu.
Wpisuję 60 i traktuję to jako limit całego RSS.
Ustawiam 100%, żeby nie zostawić niewykorzystanej pamięci.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Decyzja 6 / 8
Utworzyłem Private Endpoint. Czy mogę uznać dane za niedostępne z Internetu?
Tak, nazwa usługi jest wystarczającym dowodem.
Tak, o ile API działa z mojego laptopa.
Sprawdzam publiczny dostęp, zaporę, DNS i test odmowy z zewnątrz.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Decyzja 7 / 8
Przygotowuję wersję, która zastąpi starą kolumnę. Jak zapewnię możliwość powrotu?
Planuję zgodność danych; stosuję expand–contract i wcześniej ćwiczę powrót.
Sam poprzedni obraz na pewno wystarczy.
Usuwam bazę i liczę, że aplikacja ją odtworzy.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Decyzja 8 / 8
Budżet chmurowy ma próg 100 zł. Co naprawdę gwarantuje?
Rachunek nigdy nie przekroczy 100 zł.
To sygnał kosztowy; osobno ograniczam zasoby i maksymalne skalowanie.
Po przekroczeniu wszystkie zasoby natychmiast i bezpiecznie się wyłączą.
Wybierz odpowiedź, żeby zobaczyć wyjaśnienie.
Wróć do wyjaśnienia w rozdziale →Poprawne odpowiedzi: 0/8
Zacznij test od nowa
Wybierz odpowiedzi. Wynik pozostaje w tej karcie przeglądarki.
Dalsza praca / Z wiedzy do konkretnej zmiany
Twój system nie potrzebuje kolejnej ogólnej listy dobrych praktyk.
Potrzebuje odpowiedzi, gdzie może zgubić dane, powielić operację, ujawnić cudzy rekord albo przekroczyć możliwości infrastruktury. Potem potrzebuje kolejności napraw i testów, po których wiadomo, że zmiana działa.
W ArcCloud sprawdzamy aplikacje, integracje i środowiska chmurowe. Możemy pomóc przejść od rozpoznania problemu do jego naprawy: ustalić zakres, pokazać ryzyko na konkretnym scenariuszu i zaplanować weryfikację po wdrożeniu. Dobór audytu zależy od tego, co faktycznie wymaga sprawdzenia — publiczna strona nie pokaże całej logiki prywatnego systemu.
Chcesz wracać do takich warsztatów? Sprawdź ArcCloud Engineering Brief. Poniżej znajdziesz aktualną ofertę subskrypcji. Ten poradnik, quiz i laboratorium pozostają dostępne bez zakupu.
16 / Zajrzyj do źródła, gdy szczegół ma znaczenie
Dokumentacja, do której warto wracać
Odnośniki przy rozdziałach prowadzą do dokumentacji autorów technologii oraz OWASP. Poniższa lista porządkuje dalszą lekturę. Przed użyciem konfiguracji sprawdź wersję runtime, typ zasobu i region. Limity oraz możliwości usług zmieniają się szybciej niż podstawowe zasady projektowania.
- Microsoft: Retry pattern — kiedy ponowienie pomaga i jak może pogłębić problem.
- AWS: idempotent APIs — tożsamość operacji i bezpieczne powtórzenia.
- AWS: Transactional outbox — zapis danych i intencji publikacji.
- GitHub: podpisy webhooków — dokładna treść, podpis i sekret.
- OWASP API Security: BOLA — dostęp do konkretnego obiektu.
- OWASP: SSRF Prevention — granice pobierania zewnętrznych adresów.
- GitHub Actions: OIDC — zaufanie i krótkotrwała tożsamość pipeline.
- Microsoft: konfiguracja GC — format i zakres limitów sterty.
- Kubernetes: zasoby kontenerów — requests, limits i zachowanie przy przeciążeniu.
- Kubernetes: sondy — gotowość, start i żywotność procesu.
- Microsoft: Azure dla osób pracujących z AWS — porównanie ról usług, bez zakładania pełnej zamienności.
- Polityka wsparcia .NET — wybór wspieranej wersji do rzeczywistego produktu.
Zanim zapytasz „jakiej biblioteki użyć?”, zapytaj: co obiecujemy, komu ufamy, co może się powtórzyć i jak sprawdzimy wynik po awarii.
Materiał edukacyjny ArcCloud Studio · sprawdzony 13.10.2026 · ilustracja koncepcyjna, przykładowy system Pracownia. Rozwinięcie podstaw kodu: Czysty kod w C# i Clean Architecture.