# Złożoność cyklomatyczna w rollout gating i canary strategy

URL: https://upstreamapi.com/pl/journal/zlozonosc-cyklomatyczna-rollout-gating
Type: blog
Locale: pl
Published: 2026-07-25
Updated: 2026-07-25

---

> Złożoność cyklomatyczna liczy niezależne ścieżki w funkcji. Większość zespołów ignoruje deltę przy rollout. Delta przewiduje incident risk lepiej niż absolute score i powinna być w dashboardzie.

## Złożoność cyklomatyczna: od bramy CI do gating canary przy rollout

Złożoność cyklomatyczna liczy niezależne ścieżki w funkcji. NIST rekomenduje pułap na poziomie 10 na funkcję; większość zespołów egzekwuje coś zbliżonego w CI. Co ta liczba nie mówi, to które ze ścieżek faktycznie będą obsługiwane przez kanaryjkę zaraz po wysłaniu do 5% ruchu produkcyjnego (i ta luka to tam gdzie zaczyna się większość incydentów rollout). Potraktuj złożoność cyklomatyczną jako wejście do ryzyka rollout, nie jako regułę lint, a ta liczba zaczyna się opłacać.

## Co mierzy złożoność cyklomatyczna

Metrika McCabe'a to graf: węzły, krawędzie, komponenty spójne. Każde `if`, `for`, `while`, `case` i operator boolowski dodaje ścieżkę. Funkcja ze złożonością 25 ma co najmniej 25 liniowo niezależnych ścieżek. To nie opinia: to matematyka testowania ścieżek bazowych (basis path testing), i wyznacza ona dolną granicę liczby przypadków testowych potrzebnych do pokrycia funkcji w całości. [Rozbiórkę od Sourcegraph](https://sourcegraph.com/blog/cyclomatic-complexity-what-it-is-and-how-to-reduce-it) zawiera pełne progi ryzyka: 1-10 niskie, 11-20 umiarkowane, 21-50 wysokie, 50+ to wskaźnik wymagający rozmowy.

Większość zespołów tego progu nie osiąga. Narzędzia Coverage raportują pokrycie linii, nie pokrycie ścieżek, więc funkcja może wykazać 90% pokrycie podczas gdy połowa jej branchy nigdy się nie wykonają w CI. To ta część, którą nikt nie wpisuje w szablon PR, i to powód dla którego testy przeszły oraz to jest bezpieczne do wysłania to dwa różne twierdzenia, ale traktuje się je jak jedno.

Mamy ich wystarczająco dużo, aby dostrzegać wzór w post-mortemach: przegląd incydentu zawsze ma linię dla spalenia error budżetu i linię dla czasu detekcji. Prawie nigdy nie ma linii dla tego, jak wyglądała złożoność zmienionej funkcji zanim diff wylądował. To luka w dokumentacji, nie tylko w narzędziach. Ten brak podważa naszą zdolność do predykcji w rollout.

![Close-up of hands reviewing a code diff with nested indentation on a laptop screen](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/upstreamapi/2026-07/91e851-inline1.webp)

## Dlaczego płaska liczba złożoności ukrywa faktyczny blast radius

O tym każdy dashboard złożoności się myli: dwie funkcje mogą mieć identyczny wynik złożoności cyklomatycznej i niesamowicie różne ryzyko. Instrukcja switch z 20 gałęziami kierującymi do dobrze przetestowanych handlerów to nie ten sam zwierzak co funkcja z czterema oddzielonymi kieszeniami zagnieżdżonych warunków rozrzuconych przez 80 linii, każda trzy poziomy głęboko. CodeScene nazywa drugi kształt bumpy road, co oznacza wyboista droga, i nazwa jest dokładna. To głębokość zagnieżdżenia, nie surowa liczba gałęzi, która obciąża pamięć pracującą i ukrywa edge case'a, o którym nikt nie pomyślał, aby przetestować. [Artykuł od CodeScene](https://codescene.com/blog/bumpy-road-code-complexity-in-context/) przechodzi przez porównanie w szczegółach i mapuje się czysto na to, co widzimy w danych rollout.

To ważne dla nas konkretnie, bo kanaryjka nie pada na średnią złożoność. Pada na jedną wyboistą funkcję, którą dotknął ten diff, o 2 nad ranem, pod obciążeniem które nikt nie load-testował. W post-mortemach które przejrzeliśmy z zespołami platform, wzór się powtarza: funkcja pierwotnej przyczyny prawie nigdy nie ma najwyższego wyniku złożoności w repo. Ma najwyższą deltę złożoności w diffie który został wysłany. To inny sygnał, i prawie nikt go nie instrumentuje.

Dashboard, który mówi ci że złożoność codebasy ma trend spadkowy, to nie ten sam dashboard, który mówi ci że ten rollout, teraz, dotknął funkcję która właśnie podniosła o trzy nowe gałęzie zagnieżdżone. Jeden to raport zdrowia który czytasz raz na kwartał. Drugi to brama którą faktycznie chciałbyś w pipelineie deploymentu, siedząc obok SLO check, nie piętrzone w oddzielnym narzędziu do analizy statycznej które nikt nie otwiera podczas incydentu.

Rollout oparte na ringach i procentach już zakładają że niektóre dify są bardziej ryzykowne niż inne, to cały premise kanaryjki. Co większość pipelineów nie robi, to pozwolenie aby kształt złożoności diffie sam informował jak szybko ta kanaryjka powinna się poszerzać. Traktuje się to jako problem code review, a potem zapomina się o tym w momencie gdy PR się merge'a.

## Brama CI na poziomie 10, którą ustawia każdy, i dlaczego nie rusza igłę

Standardowa rada, uciskaj złożoność cyklomatyczną poniżej 10 i zawalaj build powyżej tego, to step skip który prawie każdy tech blog rekomenduje i prawie nikt nie waliduje przeciw rzeczywistym danym incydentów. To nie jest źle, dokładnie. To jest niekompletne w taki sposób który pozwala zespołom wierzyć że obsłużyli ryzyko, gdy faktycznie je tylko przesunęli.

Dwa tryby awarii, oba częste w zespołach z którymi rozmawialiśmy. Po pierwsze: granie na liczbach. Extract Method to podręcznikowy fix, i działa, złożoność cyklomatyczna na funkcję spada. Ale jeśli ekstrakcja nie zmniejsza faktycznej liczby decyzji, tylko ją relokuje przez trzy funkcje zamiast jednej, złożoność systemu się nie zmienia. Brama robi green. Blast radius się nie zmniejsza, robi się po prostu trudniejszy do zobaczenia w single diff view. Po drugie: ignorowanie kształtu. Płaska próg traktuje dispatcher z 12 gałęziami i 12-ścieżkową wyboistą drogę jako równie ryzykowne. Nie są. Dispatcher to prawdopodobnie ok, to mechaniczne routing. Wyboista droga to tam gdzie żyje twój następny rollback, bo reviewer przestał śledzić state trzy poziomy głębi w.

AI coding agents zmieniają to na gorzej zanim będzie lepiej. Devin i podobne autonomous agenty wysyłają PR szybko, a szybko często oznacza dodanie gałęzi zamiast refaktoryzacji tego które już są. To ścieżka najmniejszego oporu dla modelu optymalizującego dla passing test suite, nie dla cognitive load reviewera. Jeśli twój zespół merge'a AI-authored dify w volume, delta złożoności na PR to metrika którą chciałbyś na dashboardzie zanim się stanie finding z post-mortemów, nie po.

![Two engineers reviewing a branching flowchart drawn on a glass whiteboard](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/upstreamapi/2026-07/4479b1-inline2.webp)

## Co delta złożoności faktycznie mówi ci o burden testowania

Zapomnij na chwilę o absolute score. Liczba która przewiduje incident risk to zmiana złożoności wprowadzona przez single diff, cross-referenced przeciw czy testy które dotykają ten diff faktycznie rosły żeby to dopasować. Funkcja która idzie ze złożoności 8 do 19 w jednym PR ma, przez basis path testing math wyżej, mniej więcej podwojoną minimalną wymaganą liczbę testów. Jeśli PR dodał dwa testy, wysłałeś lukę w pokryciu przebraną za passing CI run. Ta luka się nie pojawia aż kanaryjka nie trafi traffic slice, którą 5% która executes untested branch, i wtedy to jest incident, nie code review komentarz siedzący unresolved w PR thread.

To instrumentation luka którą AI Pilot upstreamapi zbudowany żeby zamknąć na stronie rollout: gate procent kanaryjki i hold time na delcie złożoności wysłanego diffie, cross-checked przeciwko jego test delta, nie tylko na downstream error-rate SLO. Error-rate SLO mówi ci że coś już się złamało. Complexity-delta gate mówi ci że diff był bardziej skłonny aby coś złamać zanim serve live traffic, co to jedynego punktu gdzie ta informacja jest wciąż actionable. Oto kod który brama by funkcjonował.

`function canaryStep(diff: DiffMetrics): CanaryDecision {
  const complexityRisk = diff.complexityDelta / Math.max(diff.testDelta, 1);
  if (complexityRisk > 3 && diff.slo.errorBudgetBurn > 0.1) {
    return { action: "hold", trafficPct: diff.currentTrafficPct };
  }
  if (complexityRisk > 3) {
    return { action: "extend_bake_time", bakeMinutes: 45 };
  }
  return { action: "advance", trafficPct: diff.currentTrafficPct + 10 };
}`
## Czy badania w ogóle się na tym zgadzają
Nie, i to warto jasno powiedzieć. Część practitioner research stawiać się mocno przeciwko złożoności cyklomatycznej jako defect predictor w ogóle, argumentując że zespoły które optymalizują dla niższego score, często relokują złożoność gdzieś mniej widocznym. [Krytyka od GetDX](https://getdx.com/blog/cyclomatic-complexity/) robi ten case i wskazuje zespoły w kierunku developer-experience metrics zamiast tego. Nie myślimy że ten argument kills metrika, myślimy że kills metrika używana sama, jako static repo-wide score, disconnected od diffie i rollout do którego jest attached.

## Jak faktycznie byśmy gated canary na złożoności, nie tylko na error rate

Trzy rzeczy, w kolejności jak wiele friction dodają do PR. Po pierwsze, compute complexity delta per diff, nie per repo. Repo-wide averages ukrywają tę jedną funkcję która matters ten tydzień. Delta per PR jest cheap aby compute w większości static analysis tools, i to liczba która correlates z tym co faktycznie się łamie w następne 48 godzin.

Po drugie, cross-reference przeciwko test delta, nie test count. Funkcja z 40 testami i complexity jump ze 8 do 19 z zero nowymi testami to bigger risk niż fresh function ze złożonością 15 i matching coverage od day one.

Po trzecie, feed ratio do rollout pacing, nie tylko do merge approval. High complexity-delta diff nie musi być blocked przy review, plenty of legitnie complex domain logic takie jak state machines czy protocol parsers zawsze będzie score wysoko. Potrzebuje wolniejszą kanayjkę i krótszą error-budget leash, co jest rollout decision, nie code review decision.

Napisy runbook entry zanim rollout, nie po incydencie. Jeśli on-call engineer otwierający incident channel o 3 nad ranem musi reverse-engineer dlaczego clean PR właśnie spalił error budget, dokumentacja zawiodła zanim kod. Jedna-liniowy note w deploy log (complexity delta 11, test delta 1, held at 20 procent) costs nic do write i saves pierwsze piętnaście minut każdego incydent review które następuje.

![Server room corridor at night with status LEDs and a lone engineer walking away holding a tablet](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/upstreamapi/2026-07/a58df0-inline3.webp)

## Czy to warte śledzenia, czy tylko kolejny number na dashboardzie

Zależy całkowicie od tego gdzie go przypiąć. Złożoność cyklomatyczna jako repo-health trend line to głównie decoration, ładnie na quarterly slide, bezużyteczne o 2 nad ranem. Złożoność cyklomatyczna jako per-diff delta, podana do canary pacing i cross-checked przeciwko test growth, jest jednym z tańszych leading indicators które znaleźliśmy dla sygnału że ten rollout będzie page somebody.

Post-mortem będzie pytać co SLO budget wyglądał zanim rollback. Coraz częściej, nasz też pyta co complexity delta wyglądał na diffie która wysłana. Worth dodania tego pytania do twojego własnego runbook zanim incident narzuci konwersację, nie podczas retro gdy odpowiedź to wzruszenie i obietnica dodamy lepsze testy następnym razem.

## FAQ

### Czym różni się złożoność cyklomatyczna od pokrycia kodu?

Pokrycie kodu mierzy które linie się execute w testach. Złożoność cyklomatyczna mierzy ile niezależnych ścieżek musi być przetestowanych. Funkcja może mieć 90% pokrycia linii, ale 0% pokrycia ścieżek jeśli cały warunkowy branch nigdy się nie fire'a w CI.

### Dlaczego Extract Method nie zawsze rozwiązuje problem złożoności?

Extract Method redukuje złożoność per funkcję, ale może nie zmniejszyć systemu's ogólnej złożoności. Jeśli ekstrakcja tylko relokuje decyzje branching zamiast eliminować je, blast radius rollout się nie zmienia, robi się tylko trudniej do zobaczenia w review.

### Jak powinniśmy gateować canary na delcie złożoności?

Compute complexity delta od diffie, cross-check z test delta. Jeśli delta złożoności > 3x test delta, hold canary na niskim procencie i extend bake time. To zwycięstwa leading indicator dla incydentów przed error-rate SLO to zobacze.

### Czy AI agents są złe dla złożoności kodu?

AI agents jako Devin mają tendencje aby dodać branchy zamiast refactoryzować. Jeśli mergujesz AI-authored PRs, tracking complexity delta per diff jest ściśle ważne, będzie wyższe niż human-authored dify.

### Jak zmierzyć rzeczywisty impact złożoności na produkcję?

Śledź correlation między deltą złożoności PRów a czasem do detektu incydentów w następne 48 godzin. Zespoły które robiły to widzą wyraźny sygnał: high delta correlates z wyższym MTTR. To jest dane którą powinna być na dashboardzie pre-incident.