# Złożoność cyklomatyczna: pomiar i optymalizacja kodu

URL: https://formula.dog/pl/journal/zlozonosc-cyklomatyczna-2026
Type: blog
Locale: pl
Published: 2026-08-01
Updated: 2026-08-27

---

> Złożoność cyklomatyczna (CC) to liczba niezależnych ścieżek przez kod. Mierzy testowość. Wynik 1-10: norma, 11-15: uwaga, 16-20: alarm, 21+: krytyka. Naucz się liczyć ją i zmniejszać praktycznie.

## Złożoność cyklomatyczna: pomiar i optymalizacja kodu

Złożoność cyklomatyczna to liczba, która mówi ci jedno: **ile decyzji (rozgałęzień) ma twój kod**. Im więcej `if`, `else` i `case`, tym wyższy wynik, tym trudniej ten kod testować, utrzymywać i debugować.

Wzór to `M = E - N + 2P`. Brzmmi bardzo ładnie matematycznie, ale pokażemy to na żywych przykładach, a potem na narzędziach, które liczą to za ciebie.

## Czym jest złożoność cyklomatyczna i skąd się bierze

Złożoność cyklomatyczna (CC) mierzy liczbę niezależnych ścieżek przez funkcję. Stworzył ją Thomas McCabe w 1976 roku jako metrykę testości kodu. Metryka ta stała się standardem w inżynierii oprogramowania i jest używana przez zespoły na całym świecie, od startupów po gigantów technologicznych.

Im wyższy wynik CC, tym więcej testów trzeba napisać, aby pokryć wszystkie gałęzie. Pojęcie jest proste: funkcja bez żadnych warunków (CC = 1) wymaga jednego testu. Funkcja z jednym `if` (CC = 2) wymaga co najmniej dwóch testów. Funkcja z trzema zagnieżdżonymi `if` (CC = 4) wymaga już czterech testów.

W praktyce zespoły programistyczne używają CC do monitorowania złożoności kodu. Wysoka CC oznacza ryzyko: trudniej testować, trudniej zrozumieć, większe prawdopodobieństwo bugów. To metryka, która bezpośrednio wpływa na jakość produkowanego oprogramowania. Gdy CC rośnie, spada testowość, a ryzyko regresji rośnie exponencjalnie. Dlatego wiele zespołów monitoruje tę metrykę w ciągłej integracji (CI/CD).

## Wzór za złożością cyklomatyczną: M = E - N + 2P

Mathematycznie, złożoność cyklomatyczna to liczba niezależnych ścieżek przez graf przepływu sterowania. Grafem sterowania (control flow graph) jest reprezentacja wszystkich możliwych ścieżek, jakie kod może przejść w trakcie wykonania.

Wzór wygląda tak:

`M = E - N + 2P`Gdzie:

- 
**E** = liczba krawędzi w grafie (połączenia między instrukcjami)

- 
**N** = liczba węzłów (instrukcje, każda linia kodu to węzeł)

- 
**P** = liczba spójnych komponentów (zazwyczaj 1, chyba że funkcja ma wiele głównych ścieżek)

Albo **szybszy sposób praktyczny**: policz każdy punkt decyzji (każdy `if`, `while`, `for`, `case`, `catch`, operator trójargumentowy `?:`) i dodaj 1. Punkty decyzji to miejsca, gdzie kod może pójść w różne kierunki. To najszybsza metoda dla praktyków.

**Praktyczny przykład z e-commerce:**

`def process_order(order):
    if order.total > 100:           # 1 decyzja
        if order.vat_paid:          # 2 decyzje
            total = order.total
        else:
            total = order.total * 1.23
    else:
        if order.shipping_needed:  # 3 decyzje
            total = order.total + 10
        else:
            total = order.total
    return total`Liczba decyzji: 3 (trzy `if`), czyli CC = 3 + 1 = **4**. Ta funkcja ma 4 możliwe ścieżki wykonania, każda reprezentująca inny scenariusz biznesowy.

## Ręcznie: konkretny przykład z drzewem decyzji

Weźmy funkcję Pythona, która waliduje zamówienie e-commerce. Funkcja musi sprawdzić kilka warunków: czy zamówienie nie jest puste, czy klient jest pełnoletni, czy klient ma zgodę rodzica jeśli jest niepełnoletni, czy ma wystarczająco pieniędzy na opłacenie.

`def validate_order(items, customer, payment):
    if not items:                    # decyzja 1
        return False
    
    if customer.age < 18:            # decyzja 2
        if payment.type != "parental_consent":
            return False             # decyzja 3
    
    if payment.amount < sum(items):  # decyzja 4
        return False
    
    return True`**Liczenie punkt po punkcie:**

- 
`if not items` to decyzja 1: jeśli brak pozycji, zwróć False

- 
`if customer.age < 18` to decyzja 2: jeśli klient niepełnoletni

- 
`if payment.type != "parental_consent"` to decyzja 3: jeśli brak zgody rodzica

- 
`if payment.amount < sum(items)` to decyzja 4: jeśli za mało pieniędzy

Złożoność = 4 decyzje + 1 = **CC = 5**.

Na grafie przepływu sterowania byłoby to 5 niezależnych ścieżek, które kod może przejść. Każda ścieżka reprezentuje inny scenariusz biznesowy: puste zamówienie, pełnoletni, niepełnoletni bez zgody, niepełnoletni ze zgodą, brak pieniędzy.

![Diagram przepływu sterowania z węzłami decyzji](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/formula-dog/2026-08/10095b-inline1.webp)

## Co oznacza Twój wynik: normy i progi alarmowe

Thomas McCabe zaproponował te progi referencyjne, którymi posługuje się większość zespołów programistycznych na świecie:

- 
**1 do 10**: Niska złożoność. Kod łatwy do testowania i utrzymania. To, do czego dążymy. Testowanie manualne OK, testy automatyczne zalecane. Funkcje na tym poziomie są czytelne i nie stanowią zagrożenia dla jakości. To ideał.

- 
**11 do 15**: Umiarkowana złożoność. Warto się zastanowić nad refaktoringiem. Funkcja może pracować, ale polecany przegląd kodu. Testowanie powinno być bardziej dokładne, mogą się zacząć pojawiać pierwsze problemy z czytelnością. Wiele zespołów już tutaj włącza alarmy.

- 
**16 do 20**: Wysoka złożoność. Testowanie staje się pracochłonne, błędy trudne do wyłapania. Refaktoruj teraz, zanim to przytłocze projekt. Na tym poziomie kod zaczyna być koszmarny w utrzymaniu. Większość zespołów zabrania mergowania.

- 
**21+**: Krytyczna złożoność. Kod praktycznie niemożliwy do pełnego testowania i zrozumienia. Rozbij funkcję natychmiast. Nawet autorzy zapominają, co robił kod po kilku miesiącach. To jest alarm maximum.

W praktyce zespoły ustawiają własne limity. SonarQube domyślnie ostrzega przy CC > 10, zakazuje mergowania PR przy CC > 15. Facebook używa bardziej liberalnych limitów (do 20), ale małe startupy mogą chcieć ścieńszych granic (max 8). Netflix ma zaostrzony standard: maksimum CC = 7.

## Złożoność cyklomatyczna vs. złożoność poznawcza

To dwa różne zwierzęta, ale łatwo je pomylić. Obie mierzą złożoność, ale innymi metrami.

**Złożoność cyklomatyczna (CC)** liczy liczbę decyzji, czyli ile ścieżek przez kod. To miara obiektywna, możliwa do obliczenia automatycznie. Każdy narzędzie da ten sam wynik.

**Złożoność poznawcza (CC~)** mierzy trudność mentalną, czyli ile pamięci musi umysł alokować, aby zrozumieć kod. Zagnieżdżone `if` to bardziej złożone poznawczo niż płaskie sekwencje, nawet jeśli CC jest taka sama. Mierzy to, jak długo będziesz myślał nad kodem.

Przykład ilustrujący różnicę:

`# Niska CC, wysoka złożoność poznawcza:
def complex_logic(a, b, c, d, e):
    if a and b and c and d and e:  # 5 warunków, ale CC = 2
        return True
    return False

# Wysoka CC, niższa złożoność poznawcza:
if a:
    if b:
        if c:
            if d:
                if e:
                    return True  # CC = 5, ale każda decyzja wyraźna
return False`Google i SonarSource teraz preferują złożoność poznawczą do decyzji refaktoringowych, bo lepiej opisuje rzeczywisty wysiłek umysłu. CC to bardziej "testability" (testowość), CC~ to bardziej "readability" (czytelność).

![Dwie osoby robiące code review przy biurku](https://fdzlnqpwsaniezitwiuw.supabase.co/storage/v1/object/public/cms-media/formula-dog/2026-08/e9c9a1-inline2.webp)

## Złożoność cyklomatyczna w Excelu i Google Sheets

Możliwe, że nigdy nie słyszałeś o CC dla formuł, ale jest tam wszędzie i sprawia tyle samo problemów co w kodzie.

Formuła Excela z zagnieżdżonymi IF:

`=IF(B2>100, IF(C2="VIP", B2*0.8, B2*0.9), IF(B2>50, B2*0.95, B2))`To cztery decyzje: B2>100, czy VIP, oraz B2>50. CC = 4 + 1 = 5.

Formuła jest czytelna dzisiaj? Tak. Ale za rok, gdy będziesz jej szukać w dokumentacji, gdy ktoś inny będzie musiał ją edytować, gdy zmienią się progi biznesowe, będzie chaos. Wiele błędów w tablicach pochodzi stąd, że osoba pisała formułę z CC = 7, a nikt nie był w stanie jej przetestować. Formuła robi coś dziwnego, ale co? Nikt nie wie. I siedzi tam przez 3 lata, aż w końcu ktoś ją usuwa i przepisuje od nowa. To jeden z powodów, dla których duże korporacje wprowadzają audyty formuł i przeglądy jakości.

## Pięć sposobów na zmniejszenie złożoności cyklomatycznej

### 1. Klauzule ochrony (guard clauses)

Zamiast zagnieżdżać `if`, zwróć wcześnie błędy. Płaska struktura jest łatwiej czytelna.

**Przed:**

`def process(user, order, payment):
    if user:
        if order:
            if payment:
                return calculate(order)
    return None`**Po:**

`def process(user, order, payment):
    if not user: return None
    if not order: return None
    if not payment: return None
    return calculate(order)`CC spada z 3 zagnieżdżeń na 3 płaskie decyzje. Kod czyta się jak lista warunków do spełnienia, nie jak piramida zagnieżdżeń.

### 2. Wyciągaj funkcje

Jeśli masz wielkie `if` z boolowskim wyrażeniem, wyciągnij logikę do osobnej funkcji:

**Przed:**

`if validate_vat(customer) and check_shipping(order) and is_solvent(payment):
    process(order)`**Po:**

`if order_is_valid(customer, order, payment):
    process(order)

def order_is_valid(customer, order, payment):
    return (validate_vat(customer) and check_shipping(order)
            and is_solvent(payment))`CC głównej funkcji spada (jeden warunek). Nowa funkcja jest testowalna niezależnie i ma swoją nazwę opisującą logikę.

### 3. Tablica przeglądowa zamiast switch

`switch` lub seria `elif` zwiększa CC liniowo. Tablica zmniejsza ją do 1.

**Przed:**

`if status == "pending": send_email_pending()
elif status == "approved": send_email_approved()
elif status == "rejected": send_email_rejected()
elif status == "archived": log_archive()`CC = 4 + 1 = 5.

**Po:**

`actions = {
    "pending": send_email_pending,
    "approved": send_email_approved,
    "rejected": send_email_rejected,
    "archived": log_archive
}
actions.get(status, no_op)()`CC = 1. Dodajesz nowy status? Dodajesz entry, nie modyfikujesz logiki.

### 4. Podziel funkcję po liniach biznesowych

Jeśli funkcja robi 10 rzeczy pod różnymi warunkami, rozbij ją:

`# Zamiast jednej mega-funkcji z CC = 15:
validate_payment_and_ship_and_notify(order)

# Robi się:
validate_payment(order)   # CC = 3
ship_order(order)         # CC = 2
notify_customer(order)    # CC = 2`Każda funkcja jest niezależna, testowalna i czytalna. Łatwiej się zmienia, łatwiej się debuguje.

### 5. Przenieś warunki do danych

Zamist pytać kod, zapytaj dane. Reguły powinny żyć w konfiguracji:

`# Zamiast:
if user.role == "admin": perms = ["read", "write", "delete"]
elif user.role == "moderator": perms = ["read", "write"]
elif user.role == "user": perms = ["read"]

# Robi się:
role_perms = {
    "admin": ["read", "write", "delete"],
    "moderator": ["read", "write"],
    "user": ["read"]
}
perms = role_perms.get(user.role, [])`Logika kodu = 0 decyzji. Reguły żyją w konfiguracji. Dodajesz nową rolę? Dodajesz do słownika, nie edytujesz kod logiki.

## Błędy powszechne związane z wysoka CC

Wysoka złożoność cyklomatyczna to praktyczny problem, który wpływa bezpośrednio na prędkość pracy zespołu, jakość kodu i satysfakcję z pracy. Poniżej znajduje się lista problemów powszechnych:

- 
**Trudność w pisaniu testów**: każda ścieżka wymaga osobnego testu. Przy CC = 20, potrzebujesz co najmniej 20 testów, każdy z własnym setupem danych.

- 
**Wysokie ryzyko regresji**: zmiana jednego warunku może zepsuć wiele scenariuszy. Tester nie ma szansy wszystkiego pokryć.

- 
**Problemy z czytelnością**: nowa osoba w zespole spędzi dni na zrozumieniu logiki. Zwiększa się czas onboardingu.

- 
**Trudności w debugowaniu**: która ścieżka jest odpowiedzialna za buga? To trudne w kodzie o CC > 15.

- 
**Wypaląnie zespołu**: utrzymywanie skomplikowanego kodu jest wyczerpujące.

## Narzędzia do analizy CC

Nie musisz liczyć CC ręcznie. Narzędzia robią to za ciebie:

- 
**SonarQube**: najpopularniejszy, raporty per-funkcja, integracja CI/CD

- 
**Pylint** (Python): raport tekstowy, CC dla każdej funkcji

- 
**ESLint** + `eslint-plugin-complexity` (JavaScript): real-time feedback

- 
**Clang** (C/C++): komplexity warnings przy `clang-check`

- 
**Radon** (Python): CLI, prosta liczba dla każdej funkcji

- 
**Cognitive Complexity** (SonarSource): nowsza alternatywa

Uruchom jedno z tych narzędzi na swoim kodzie. Szansa na znalezienie czegoś z CC > 20? Bardzo wysoka. Wtedy widzisz dokładnie, gdzie refaktorować i co zmienić w kodzie.

## Polecane narzędzia do analizy

Aby śledzić złożoność cyklomatyczną w produkcji, warto mieć narzędzia w toolsetcie:

## Podsumowanie

Złożoność cyklomatyczna to liczba, która mówi ci dokładnie, czy kod będzie można testować i utrzymywać bez dodatkowych kłopotów. Wzór (M = E - N + 2P) to liczba decyzji + 1.

Wynik 1-10: norma. 11-15: uwaga. 16-20: zły. 21+: alarmowo.

Warto wiedzieć, czego szukać: zagnieżdżone `if`, długie `switch`, warunki z wieloma `and` i `or`. Te rzeczy robią kod trudnym do czytania i testowania.

Jeśli pracujesz w Excel lub Google Sheets, to samo dotyczy formuł. Formułę z CC = 7 przeczyta każdy. Formułę z CC = 12 nie przeczyta nikt za rok.

## FAQ

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

Złożoność cyklomatyczna liczy punkty decyzji. Złożoność poznawcza mierzy trudność mentalną. Zagnieżdżone if ma niską CC, wysoką poznawczą.

### Jaki jest bezpieczny próg złożoności cyklomatycznej?

1-10 to norma, 11-15 to uwaga, 16-20 to już wysoko, 21+ to alarm. Większość zespołów ustawia limit na 10 lub 15.

### Jak policzyć CC ręcznie?

Policz każdy punkt decyzji (if, while, for, case, catch) i dodaj 1. Lub użyj wzoru M = E - N + 2P.

### Czy złożoność cyklomatyczna ma znaczenie w Excelu?

Tak. Formuła z wieloma IF ma wysoką CC. Formuła z CC > 10 jest trudna do testowania i utrzymywania.

### Jakie narzędzie powinien użyć do mierzenia CC?

SonarQube (uniwersalne), Pylint (Python), ESLint + plugin (JavaScript), Radon (Python CLI). Wybierz wspierające Twój stos.

### Czy refaktorowanie kodu tylko aby obniżyć CC to zawsze dobra idea?

Nie zawsze. Niska CC ma sens w produkcji. W prototypach wyższa CC może być OK. Ale jeśli CC > 15, to kod zyska na refaktoringu.

### Czy CC zmienia się w zależności od języka programowania?

Formuła jest ta sama (E - N + 2P), ale języki liczą węzły inaczej. Wynik jest porównywalny, może być niewielka różnica między narzędziami.