Przejdź do głównej zawartości

Rozwój systemów rozproszonych z AI

Rozwój systemów rozproszonych z agentami AI zaczyna się od kontraktu: najpierw zmienia się kontrakt OpenAPI lub AsyncAPI, a dopiero potem kod, i każdą zmianę sprawdza automat. Claude Code, Codex i Cursor budują potem po jednym kroku sagi, jednym konsumencie albo jednej wersji schematu naraz, a każdy z nich zamyka test kontraktu, test kompensacji albo ślad, który przechodzi przez wszystkie przeskoki.

Dodajesz jedno pole do API order-service i trzy inne usługi zaczynają zwracać 500 na stagingu. Ślad urywa się po dwóch przeskokach, bo konsument Kafki nie przekazał kontekstu śledzenia, a saga płatności pominęła zwrot środków. Agent, którego prowadziłeś, widział tylko otwarte repozytorium, więc potraktował order-service jak samodzielną aplikację.

Ten przewodnik jest dla programistów, którzy odpowiadają za jedną usługę w systemie złożonym z wielu repozytoriów, oraz dla tech leadów, którzy muszą sprawić, żeby zmiany agenta na granicach usług dało się bezpiecznie scalać. Zakłada, że piszesz specyfikację przed promptem; jeśli nie, zacznij od spec-driven development.

  • Sekcję integracyjną do CLAUDE.md, AGENTS.md albo reguły projektu w Cursorze, która mówi agentowi pracującemu w jednym repozytorium, jakich kontraktów ma przestrzegać
  • Pięciokrokową pętlę zmiany granicy usługi, w której każdy krok kończy się kontrolą głośno sygnalizującą błąd
  • Prompty do skopiowania: endpoint od kontraktu, krok sagi z kompensacją, konsument zachowujący ślad, migracja schematu z podwójną publikacją i plan zmian w wielu repozytoriach
  • Bramki CI, dzięki którym scalasz zmianę agenta między usługami bez czytania każdej linii: lint kontraktu, testy kontraktowe, testy kompensacji i test ciągłości śladu
  • Zweryfikowane serwery MCP (Sentry, Grafana, Kubernetes), dzięki którym agent czyta żywą telemetrię zamiast zgadywać
  • Procedury naprawcze dla urwanych śladów, osieroconego stanu sagi, dryfu kontraktu i podrobionych pakietów MCP

Mikrousługi dzielą jeden system między repozytoria, języki i zespoły. Agent wczytuje jedno repozytorium, więc jego domyślne założenie, że ta usługa działa samodzielnie, jest błędne dokładnie tam, gdzie psuje się produkcja. Rozwiązanie: trzymaj kontrakty wewnątrz każdego repozytorium (katalog contracts/, submoduł Git albo opublikowany pakiet) i wymień je w pliku, który agent zawsze czyta.

Dodaj sekcję integracyjną do CLAUDE.md. Claude Code czyta też AGENTS.md, gdy w repozytorium nie ma CLAUDE.md (od v2.1.277 w kanale latest), więc jeden wspólny plik może obsłużyć Claude Code i Codeksa.

## Service boundaries (order-service)
Contracts live in contracts/ and are the source of truth:
- contracts/payment-api.yaml (OpenAPI 3.1): payment-service REST API
- contracts/inventory-events.yaml (AsyncAPI): events we consume
- contracts/order-events.yaml (AsyncAPI): events we publish
Rules:
- Never hand-write an HTTP client. Regenerate with `npm run generate-clients`.
- Change the contract first, in its own commit, and stop for review.
- Every consumer extracts trace context from message headers; every producer injects it.
- Every saga step ships with a compensation and a test that triggers it.
- Run `npm run contracts:check` and `npm test` before you say a task is done.

Każda zmiana granicy przechodzi tę samą pętlę. Każdy krok kończy się kontrolą, którą uruchamiasz, a nie diffem, który czytasz.

  1. Zmień kontrakt i nic poza nim. Agent edytuje contracts/*.yaml w osobnym commicie i się zatrzymuje. Przeglądasz krótki diff YAML-a zamiast kodu handlerów zbudowanego na jego podstawie.
  2. Zabezpiecz kontrakt bramką. CI lintuje specyfikację i uruchamia testy kontraktowe dla każdego konsumenta, więc zmiana łamiąca zgodność pada, zanim ktokolwiek ją zaimplementuje.
  3. Generuj, a potem implementuj pod testy. Klienci i szkielety serwera powstają z kontraktu; agent pisze logikę biznesową, dopóki padające testy nie przejdą.
  4. Udowodnij ścieżkę w czasie działania. Test ciągłości śladu, a dla sag test kompensacji, pokazują, że zmiana działa przez cały przeskok, a nie tylko wewnątrz usługi.
  5. Wdrażaj w zgodnych krokach. Producenci i konsumenci wdrażają się niezależnie, więc stary i nowy format muszą być jednocześnie poprawne, dopóki metryka nie pokaże, że przeszli wszyscy konsumenci.

Zatrzymanie w kroku 4 to sedno tego promptu. Przegląd kontraktu jest krótki i przesądza o wszystkim dalej; przegląd handlera jest długi i przesądza o niewielu rzeczach.

Większość dryfu, zanim trafi na staging, łapią dwie kontrole: linter specyfikacji przy każdej zmianie kontraktu oraz testy kontraktowe sterowane przez konsumenta w Pact (@pact-foundation/pact w npm, 17.1.4 na 2026-09-26), uruchamiane w potoku zarówno konsumenta, jak i dostawcy. Połącz je w jeden skrypt, żeby agent i CI uruchamiały to samo polecenie:

{
"scripts": {
"contracts:check": "spectral lint contracts/*-api.yaml && asyncapi validate contracts/order-events.yaml && npm run test:pact"
}
}

spectral pochodzi z @stoplight/spectral-cli i wymaga pliku .spectral.yaml w katalogu głównym repozytorium (minimum to extends: ["spectral:oas"]); asyncapi pochodzi z @asyncapi/cli. Gdy ręcznie poprawiany klient i tak się rozjedzie, poproś o audyt, który daje dowody, a nie zapewnienia:

Saga to miejsce, w którym kod rozproszony napisany przez agenta najczęściej wygląda dobrze, a działa źle. Scenariusz pozytywny (happy path) działa; kompensacji brakuje, wykonuje się dwa razy albo nie ma limitu czasu. Buduj po jednym kroku i każ agentowi napisać test wyzwalający kompensację przed kodem, który ją wykonuje.

To, jak utrzymać tę pętlę krótką, zależy od narzędzia:

Dodaj hook PostToolUse do .claude/settings.json, żeby zestaw testów sagi uruchamiał się po każdej edycji, a regresja we wcześniejszym kroku wychodziła w tej samej turze. To kod wyjścia 2 przekazuje błąd z powrotem do Claude; każdy inny niezerowy kod tylko pokazuje wynik tobie:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "npm test -- saga --silent 1>&2 || exit 2" }]
}
]
}
}

Projektowanie hooków i ich koszt opisuje strona hooki agenta.

Jak zachować kontekst śladu przez asynchroniczne przeskoki?

Dział zatytułowany „Jak zachować kontekst śladu przez asynchroniczne przeskoki?”

Powracający błąd produkcyjny w przepływach opartych na zdarzeniach to ślad, który się urywa: konsument czyta wiadomość z Kafki, ale nie wyciąga kontekstu śledzenia z nagłówków, więc każdy kolejny span zaczyna nowy ślad. Zrób z propagacji wymaganie sprawdzane testem, a nie uwagę w prompcie.

Zinstrumentuj jedną usługę od początku do końca, zanim zrobisz z tego szablon. Poproś agenta o dodanie automatycznej instrumentacji OpenTelemetry (@opentelemetry/sdk-node z @opentelemetry/auto-instrumentations-node) do jednej usługi, eksport przez OTLP do lokalnego Jaegera, wysłanie jednego żądania i podanie identyfikatora śladu, który masz znaleźć. Gdy ślad pojawi się razem z własnym spanem, ten sam moduł staje się wspólnym pakietem importowanym przez pozostałe usługi. Stronę operacyjną, czyli dashboardy i alerty, opisuje monitorowanie i obserwowalność.

Jak zmienić schemat zdarzenia, nie psując konsumentów?

Dział zatytułowany „Jak zmienić schemat zdarzenia, nie psując konsumentów?”

Format danych przekraczający granicę usługi zmienia się sekwencją, a nie jednym commitem. Oba formaty pozostają poprawne, dopóki metryka, a nie założenie, nie pokaże, że przeszli wszyscy konsumenci.

  1. Dodaj nową wersję schematu obok starej w kontrakcie.
  2. Wdróż konsumentów, którzy akceptują obie wersje.
  3. Wdróż producenta publikującego obie wersje (albo nową wersję z zachowanymi starymi polami).
  4. Obserwuj metrykę dla każdego konsumenta pokazującą, którą wersję czyta każda usługa, aż stara spadnie do zera.
  5. Przestań publikować starą wersję, a potem usuń ją z kontraktu.

Jak skoordynować jedną funkcję w kilku repozytoriach?

Dział zatytułowany „Jak skoordynować jedną funkcję w kilku repozytoriach?”

Funkcja taka jak punkty lojalnościowe dotyka customer, order, payment i notification. Najpierw, w jednej sesji planowania, poproś o kontrakty i kolejność zależności, a potem uruchom po jednym ograniczonym zadaniu implementacyjnym na repozytorium.

Po zatwierdzeniu kontraktów narzędzia rozdzielają pracę na różne sposoby. W pętlach Claude Code i Codeksa ../contracts to lokalny checkout współdzielonego repozytorium kontraktów (org/service-contracts) z zatwierdzonymi zmianami w loyalty/, leżący obok repozytoriów usług:

Prowadź każde repozytorium w trybie headless, z dostępem do narzędzi zawężonym do tego, czego wymaga zadanie:

Okno terminala
for svc in customer order payment notification; do
(cd "../$svc-service" && claude -p \
"Implement the loyalty-points changes for this service per ../contracts/loyalty/. Add or update the Pact tests the plan lists. Run npm run contracts:check and npm test; stop and report if either fails." \
--add-dir ../contracts \
--allowedTools "Read" "Edit" "Write" "Bash(npm test:*)" "Bash(npm run contracts:check)" \
--disallowedTools "Edit(../contracts/**)" "Write(../contracts/**)")
done

--add-dir daje agentowi dostęp narzędzi do ../contracts, a dozwolone Edit i Write działałyby także tam. Reguły --disallowedTools blokują zapis do zatwierdzonych kontraktów, a reguły odmowy mają pierwszeństwo przed regułami zezwolenia. Regułę CODEOWNERS albo kontrolę CI w repozytorium kontraktów zachowaj jako zabezpieczenie awaryjne i odrzucaj każde uruchomienie, które ich dotknie.

Przy więcej niż kilku repozytoriach zajrzyj do wzorców orkiestracji: rozdzielanie zadań, ponowienia i kolejność scalania.

Agent, który może odpytać błędy, metryki i stan klastra, wnioskuje z dowodów, a nie z twojego zrzutu ekranu. W pracy deweloperskiej trzymaj te serwery w trybie tylko do odczytu: agent bada, a produkcję zmieniasz ty albo CI.

SerwerCo daje agentowiPakiet lub endpoint (sprawdzone 2026-09-26)
Sentry (oficjalny)Zgłoszenia, zdarzenia, ślady, wydaniaZdalny https://mcp.sentry.dev/mcp (OAuth); @sentry/mcp-server 0.42.0 dla Sentry hostowanego u siebie
Grafana (oficjalny)Dashboardy, zapytania do Prometheusa i Loki, incydentyPyPI mcp-grafana 1.6.3 (uvx mcp-grafana) albo Docker grafana/mcp-grafana; --disable-write włącza tryb tylko do odczytu
Kubernetes (społecznościowy, organizacja containers)Pody, logi, zdarzenia we wszystkich kontekstach kubeconfignpm kubernetes-mcp-server 0.0.67; ustawienia takie jak read_only = true trafiają do pliku TOML przekazanego przez --config

Aby serwer Kubernetes działał tylko do odczytu i udostępniał wyłącznie podstawowe narzędzia, zapisz te dwie linie jako ~/.config/k8s-mcp.toml:

read_only = true
toolsets = ["core"]
Okno terminala
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
claude mcp add grafana -e GRAFANA_URL="$GRAFANA_URL" \
-e GRAFANA_SERVICE_ACCOUNT_TOKEN="$GRAFANA_SERVICE_ACCOUNT_TOKEN" \
-- uvx mcp-grafana --disable-write
claude mcp add kubernetes -- npx -y kubernetes-mcp-server@latest --config ~/.config/k8s-mcp.toml

Skąd wiesz, że zmiana działa, jeśli nie czytasz każdego diffu?

Dział zatytułowany „Skąd wiesz, że zmiana działa, jeśli nie czytasz każdego diffu?”

Każde ryzyko w zmianie rozproszonej ma kontrolę, która zawodzi samodzielnie. Diff czytasz tylko w kontrakcie i plikach testów; resztę udowadniają bramki.

RyzykoKontrola, która to udowadniaGdzie działaKto zatwierdza
Zmiana API lub zdarzenia łamiąca zgodnośćcontracts:check: lint specyfikacji plus testy Pact w potokach konsumenta i dostawcyKażdy PRWłaściciel usługi zatwierdza commit z kontraktem
Klient rozjechany z kontraktemGenerowani klienci i tabela audytu kontraktu opisana wyżejKażdy PRCI (build pada)
Brakująca lub podwójna kompensacjaTest dla każdego kroku sagi, który wstrzykuje błąd w kolejnym kroku i sprawdza kompensację dokładnie razKażdy PRAutor; recenzent sprawdza, że test istnieje i pada bez kodu
Urwany ślad na przeskokuTest ciągłości śladu ze znanym traceparentKażdy PR konsumentów i producentówCI
Konsumenci wciąż na starym schemacieLicznik schema_version dla każdego konsumentaStaging i produkcjaTech lead zatwierdza usunięcie starej wersji
Złe wdrożenieCanary z automatycznym wycofaniem, gdy rośnie odsetek błędów lub opóźnieniePotok wdrożeniowyCI; próg ustala dyżurny

Test potwierdzający kompensację jest tyle wart, ile jego asercja, więc wymagaj od agenta pokazania przebiegu padającego przed zielonym. Jak ufać testom, których nie czytałeś, opisuje strona siła wyroczni; bramki wdrożenia – progressive delivery.

Co się psuje w systemach rozproszonych budowanych przez agenta?

Dział zatytułowany „Co się psuje w systemach rozproszonych budowanych przez agenta?”
  1. Ślad urywa się na granicy asynchronicznej. Żądanie pokazuje w Jaegerze dwa przeskoki, a potem znika. Konsument nie wyciągnął kontekstu śledzenia z nagłówków wiadomości. Znajdź w konsumencie odczyt nagłówków, dodaj go promptem o konsumencie zachowującym ślad i zachowaj test, który sprawdza ten sam traceId po obu stronach. Nie akceptuj „dodałem tracing” bez tego testu.
  2. Saga zostawia osierocony stan. Płatność przeszła, a rezerwacja magazynowa nigdy nie została zwolniona. Odtwórz problem, wstrzykując błąd w kroku po tym, który podejrzewasz, i sprawdź, że kompensacja wykonuje się dokładnie raz. Zbuduj ten krok od nowa promptem „najpierw padający test”.
  3. Agent traktuje usługę jak samodzielną. Wygenerowany kod wymyśla kształt endpointu albo pomija konwencje ponowień i dead-letter. W repozytorium brakuje sekcji integracyjnej w CLAUDE.md, AGENTS.md albo regule Cursora; dodaj ją i wymień pliki kontraktów, zanim obwinisz model.
  4. Agent zalicza test, osłabiając go. Obok poprawki pojawia się zmieniony expect, nowy skip albo wydłużony timeout. Chroń testy kontraktów i sag przed edycją przez agenta (reguła CODEOWNERS albo hook blokujący zapis) i zajrzyj do ochrony wyroczni.
  5. Zmiana kontraktu psuje konsumentów na produkcji. Pominięto fazę podwójnej publikacji. Wycofaj producenta, publikuj obie wersje i usuń starą dopiero wtedy, gdy jej licznik konsumentów pokazuje zero.
  6. Projekt to rozproszony monolit. Dwie usługi piszą do tej samej tabeli albo trzy muszą wdrażać się razem. Agenci nie zgłaszają tego sami. Zapytaj: „List every place two services share a database, a write path, or a lockstep deploy” i rozwiąż te miejsca, zanim zaczniesz dalej dzielić system. Projektowanie granic opisuje domain-driven design z AI.
  7. Serwer MCP się łączy, ale nic nie zwraca. Brakuje tokenu albo token wygasł, nazwa zmiennej środowiskowej jest błędna (GRAFANA_SERVICE_ACCOUNT_TOKEN, a nie przestarzały GRAFANA_API_KEY) albo logowanie OAuth do Sentry nie zostało dokończone. Uruchom claude mcp list lub codex mcp list, powtórz logowanie, a jeśli npm view pokazuje nieznanego wydawcę, zainstalowałeś podróbkę.