Yazdığınız her fonksiyonun bir karmaşıklık puanı vardır. Fonksiyon karar noktası içermiyorsa, hiçbir ifIçinde elsedöngü yok, yok switchDöngüsel karmaşıklığı 1'dir: kodda tam olarak bir yol vardır. Bir tane ekleyin. if Bir ifade eklenirse karmaşıklığı 2 olur. Bir tane daha eklenirse 3 olur. Bir fonksiyon, iç içe geçmiş birkaç seviyede bir düzine koşullu dal biriktirdiğinde, döngüsel karmaşıklığı 15 veya 20 olabilir ve tamamen test edilmesi, her biri farklı bir yürütme yolunu kapsayan karşılık gelen sayıda test senaryosu gerektirir.
Döngüsel karmaşıklık (CC), 1976 yılında Thomas J. McCabe tarafından bir programın kontrol akışının mantıksal karmaşıklığının nicel bir ölçüsü olarak tanıtılmıştır. Uygulamada en kullanışlı kod kalitesi ölçütlerinden biri olmaya devam etmektedir çünkü sonuçları somut ve eyleme geçirilebilir niteliktedir: bir karmaşıklık puanı, tam yol kapsamı için gereken minimum test senaryosu sayısını gösterir, kodun anlaşılmasının ve değiştirilmesinin ne kadar zor olacağını tahmin eder ve keşfedilmemiş kusurlar içerme olasılığı en yüksek olan fonksiyonları belirler. Bu kılavuz, formülü, eşikleri, dile özgü örnekleri, karmaşıklığı gerçekten azaltan yeniden düzenleme tekniklerini ve otomatik olarak nasıl ölçülüp izleneceğini ele almaktadır.
SMART TS XL
Siklomatik Karmaşıklığı Yönetmenize, Performansı Optimize Etmenize ve Gizli Hataları Önlemenize Yardımcı Olur
DAHA FAZLASINI ÖĞRENİN…Döngüsel Karmaşıklık Nedir?
Döngüsel karmaşıklık, bir programın kaynak kodundaki doğrusal olarak bağımsız yolların sayısını ölçer. Thomas J. McCabe bunu grafik teorisinden türetmiştir: her program, düğümlerin ifadeler ve kenarların aralarındaki olası akışlar olduğu bir kontrol akış grafiği olarak temsil edilebilir. Formülü şöyledir:
CC = E - N + 2P
Nerede:
- E = kontrol akış grafiğindeki kenar sayısı
- N = düğüm sayısı
- P = bağlı bileşen sayısı (genellikle tek bir işlev için 1)
Pratik hesaplamalar için daha basit bir eşdeğeri mevcuttur: CC = karar noktalarının sayısı + 1. Her if, else if, while, for, case, catch, &&, ve || Bir karar noktası daha ekler. Herhangi bir fonksiyonun başlangıç CC değeri 1'dir.
Java
// CC = 1: no decision points
public String greet(String name) {
return "Hello, " + name;
}
// CC = 3: two decision points (two if statements)
public String classify(int score) {
if (score >= 90) return "Excellent";
if (score >= 70) return "Satisfactory";
return "Needs improvement";
}
// CC = 5: four decision points (three conditions + one loop)
public double calculateTotal(List<Item> items, boolean isMember, boolean isHoliday) {
double total = 0;
for (Item item : items) { // +1
total += item.getPrice();
}
if (isMember) total *= 0.9; // +1
if (isHoliday) total *= 0.95; // +1
if (total > 100) total -= 5; // +1
return total;
}
Eşik Değerler: Hangi Puan Kabul Edilebilir?
McCabe'in en yaygın olarak alıntılanan orijinal kılavuzu, dört risk seviyesini tanımlar:
| BİDB Puanı | Risk seviyesi | Yorumlama |
|---|---|---|
| 1 - 10 | Düşük | Basit, iyi yapılandırılmış, test edilmesi kolay |
| 11 - 20 | ılımlı | Daha karmaşık; daha fazla test çabası gerektiriyor. |
| 21 - 50 | Yüksek | Karmaşık ve test edilmesi zor; yeniden yapılandırma önerilir. |
| > 50 | Çok Yüksek | Pratikte test edilemez; ciddi kalite riski |
10 eşiği, CI/CD kalite kontrollerinde en yaygın olarak uygulanan sınırdır. SonarQube'un varsayılan bilişsel karmaşıklık eşiği 15'tir (ilgili ancak farklı bir ölçüt). Güvenlik açısından kritik sistemler için NIST yönergeleri, modül başına maksimum 10 önermektedir.
Önemli bir ayrıntı: CC, anlamsal karmaşıklığı değil, yapısal karmaşıklığı ölçer. Karmaşık bir finansal hesaplamayı uygulayan CC = 8 değerine sahip bir fonksiyon, basit savunma kontrollerinden oluşan CC = 15 değerine sahip bir fonksiyondan daha anlaşılması zor olabilir. CC'yi nihai bir karar olarak değil, araştırma için bir sinyal olarak kullanın.
Farklı Dillerde Döngüsel Karmaşıklık
Python
Python'ın CC'ye katkıda bulunan karar yapıları: if, elif, else (sayılmıyor, herhangi bir koşulu yok) for, while, try/except (her biri except sayımlar), with (sayılmaz) ve mantıksal operatörler and/or şartlar altında.
piton
# CC = 1
def format_name(first: str, last: str) -> str:
return f"{first} {last}"
# CC = 4: three decision points
def calculate_discount(price: float, is_member: bool, is_holiday: bool) -> float:
discount = 0.0
if is_member: # +1
discount += 0.10
if is_holiday: # +1
discount += 0.05
if price > 100: # +1
discount += 0.02
return price * (1 - discount)
# CC = 6: five decision points (list comprehension counts as a loop)
def process_orders(orders: list[dict]) -> list[dict]:
return [
{**order, "total": order["qty"] * order["price"]} # +1 (comprehension)
for order in orders
if order["qty"] > 0 # +1 (filter condition)
if order["price"] > 0 # +1 (second filter)
]
Python CC ölçümü için araçlar: radon (radon cc src/ -s), flake8-cognitive-complexity, pylint Karmaşıklık eklentisi ile SonarQube Python analizi.
Java
Java
// CC = 7: complex authentication with multiple conditions
public AuthResult authenticate(String userId, String password, boolean isMfa) {
if (userId == null || password == null) return AuthResult.INVALID; // +2 (||)
User user = userRepository.findById(userId);
if (user == null) return AuthResult.NOT_FOUND; // +1
if (!user.checkPassword(password)) return AuthResult.WRONG_PASSWORD;// +1
if (isMfa && !user.hasMfaEnabled()) return AuthResult.MFA_REQUIRED; // +2 (&&)
return AuthResult.SUCCESS;
}
// CC = 1 + 2 + 1 + 1 + 2 = 7
Azaltmak:
Java
// After refactoring: CC = 3 (main method) + small helpers with CC = 2 each
public AuthResult authenticate(String userId, String password, boolean isMfa) {
if (hasInvalidInputs(userId, password)) return AuthResult.INVALID;
User user = findVerifiedUser(userId, password);
if (user == null) return AuthResult.WRONG_PASSWORD;
if (requiresMfa(user, isMfa)) return AuthResult.MFA_REQUIRED;
return AuthResult.SUCCESS;
}
private boolean hasInvalidInputs(String userId, String password) {
return userId == null || password == null; // CC = 2
}
private boolean requiresMfa(User user, boolean isMfa) {
return isMfa && !user.hasMfaEnabled(); // CC = 2
}
Java CC için araçlar: Checkstyle, PMD, SonarQube, IntelliJ IDEA entegre edilmiş, SMART TS XL.
C# ve TypeScript
C# ve TypeScript, Java ile aynı kuralları izler. Temel eklemeler: C#'daki LINQ ifade maddeleri ve TypeScript'teki üçlü zincirler, her ikisi de karar noktaları ekler.
csharp
// CC = 5: switch with four cases
public decimal GetShippingCost(string zone) => zone switch {
"domestic" => 5.99m, // +1
"eu" => 15.99m, // +1
"international" => 29.99m, // +1
"express" => 49.99m, // +1
_ => throw new ArgumentException($"Unknown zone: {zone}")
};
COBOL
COBOL'un CC'ye katkıda bulunan karar yapıları: IF/ELSE, EVALUATE WHEN (her bir WHEN maddesi), PERFORM UNTIL, PERFORM VARYING ... WITH TEST BEFORE/AFTER, AT END, ON EXCEPTION, NOT ON EXCEPTION, ON SIZE ERROR.
COBOL
CALCULATE-DISCOUNT.
IF WS-CUSTOMER-TYPE = 'GOLD' *> +1
IF WS-PURCHASE-AMT > 1000 *> +1
COMPUTE WS-DISCOUNT = 0.20
ELSE *> (no increment)
COMPUTE WS-DISCOUNT = 0.15
ELSE IF WS-CUSTOMER-TYPE = 'SILVER' *> +1
COMPUTE WS-DISCOUNT = 0.10
ELSE *> (no increment)
COMPUTE WS-DISCOUNT = 0.05
END-IF
EVALUATE TRUE
WHEN WS-REGION = 'NORTH' PERFORM APPLY-REGIONAL-RATE *> +1
WHEN WS-REGION = 'SOUTH' PERFORM APPLY-SOUTHERN-RATE *> +1
END-EVALUATE.
*> Total CC = 1 + 5 = 6
COBOL'un ayrıntılı sözdizimi, paragrafların genellikle modern dillerdeki eşdeğer fonksiyonlardan daha uzun olduğu anlamına gelir. Paragraf başına 50'nin üzerinde CC içeren COBOL programları, eski kod tabanlarında yaygındır ve hem yeniden düzenleme hem de modernizasyon planlaması için en yüksek öncelikli hedefleri temsil eder.
Döngüsel Karmaşıklığın Hesaplanması: Üç Yöntem
Yöntem 1: Karar noktalarını + 1 olarak sayın. En hızlı manuel yöntem. Her birini sayın. if, else if, while, for, case, catch, &&, || Fonksiyonun içinde. Fonksiyonun kendisi için 1 ekleyin.
Yöntem 2: Kontrol akış grafiği Fonksiyonu bir grafik olarak çizin: her ifade veya blok için bir düğüm, her kontrol akışı için kenarlar. Uygulayın CC = E - N + 2.
Yöntem 3: Otomatik araç Önemsiz bir fonksiyonun ötesindeki her şey için tek pratik yöntem. Çoğu statik analiz aracı, CC'yi otomatik olarak hesaplar ve CI/CD işlem hatlarına entegre eder.
Karmaşıklığı Gerçekten Azaltan Yeniden Yapılandırma Teknikleri
Koruma Maddeleri (Erken Dönüşler)
Ön koşullar başarısız olduğunda koruma maddeleri fonksiyonu erken sonlandırarak else dallanmalarını ortadan kaldırır ve iç içe geçme derinliğini azaltır.
piton
# Before: deeply nested, CC = 5
def process_order(order):
if order is not None:
if order.is_valid():
if order.has_stock():
if order.payment_cleared():
return fulfill_order(order)
else:
return "Payment failed"
else:
return "Out of stock"
else:
return "Invalid order"
else:
return "No order"
# After: flat, CC = 5 (same complexity, dramatically better readability)
def process_order(order):
if order is None: return "No order"
if not order.is_valid(): return "Invalid order"
if not order.has_stock(): return "Out of stock"
if not order.payment_cleared(): return "Payment failed"
return fulfill_order(order)
Kod karmaşıklığı azalmaz, aynı karar noktaları mevcuttur, ancak kod çok daha kolay okunabilir ve test edilebilir hale gelir. Gerçek kod karmaşıklığı azaltımı, karar noktalarını sadece yeniden düzenlemek değil, ortadan kaldırmayı gerektirir.
Çıkarma Yöntemleri
Kararların mantıksal gruplarını adlandırılmış metotlara taşımak, çağıran fonksiyonun kod karmaşıklığını azaltırken, karmaşıklığı daha küçük, test edilebilir birimlere dağıtır.
Java
// Before: one method doing everything, CC = 9
public double calculateInvoiceTotal(Invoice invoice, Customer customer) {
double subtotal = 0;
for (LineItem item : invoice.getItems()) {
subtotal += item.getQuantity() * item.getUnitPrice();
if (item.isTaxable()) subtotal += item.getPrice() * 0.1;
}
if (customer.isMember()) subtotal *= 0.9;
if (customer.hasVoucher()) subtotal -= customer.getVoucherValue();
if (subtotal < 0) subtotal = 0;
return subtotal;
}
// After: main method CC = 4, helpers have CC = 2-3 each
public double calculateInvoiceTotal(Invoice invoice, Customer customer) {
double subtotal = computeLineItemTotal(invoice.getItems());
subtotal = applyCustomerDiscounts(subtotal, customer);
return Math.max(0, subtotal);
}
Koşullu İfadelerin Polimorfizmle Değiştirilmesi
Bir fonksiyon tür veya duruma bağlı olarak dallandığında, polimorfizm dallanmayı tamamen ortadan kaldırır.
Java
// Before: switch on payment type, CC grows with each new type
public void processPayment(String type, double amount) {
switch (type) {
case "CREDIT": processCreditCard(amount); break;
case "PAYPAL": processPayPal(amount); break;
case "CRYPTO": processCrypto(amount); break;
default: throw new IllegalArgumentException("Unknown type: " + type);
}
}
// After: new payment types require no changes to this method, CC = 1
public interface PaymentProcessor {
void process(double amount);
}
public void processPayment(PaymentProcessor processor, double amount) {
processor.process(amount); // no branching
}
Karmaşık Koşullu İfadelerin Ayrıştırılması
Karmaşık mantıksal ifadeleri, amaçlarını ortaya koyan adlandırılmış yöntemlere dönüştürün.
piton
# Before: dense boolean logic, hard to understand, easy to mis-test
if user.age >= 18 and user.country in ALLOWED_COUNTRIES and not user.is_banned and user.verified:
grant_access()
# After: named predicate, self-documenting, unit-testable independently
def is_eligible_for_access(user: User) -> bool:
return (
user.age >= 18
and user.country in ALLOWED_COUNTRIES
and not user.is_banned
and user.verified
)
if is_eligible_for_access(user):
grant_access()
CI/CD İşlem Hatlarında Döngüsel Karmaşıklık
CI/CD işlem hatlarında otomatikleştirilmiş CC uygulaması, kod incelemeleri arasında görünmez bir şekilde karmaşıklığın birikmesini önler.
tatlım
# GitHub Actions: fail PR if any function exceeds CC threshold
name: Code Quality
on: [pull_request]
jobs:
complexity-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install radon (Python CC tool)
run: pip install radon
- name: Check cyclomatic complexity
run: |
radon cc src/ --min C --show-complexity
# Fails if any function has CC grade C (11-15) or worse
radon cc src/ --min C --total-average | grep -q "Average complexity" \
&& echo "Complexity check passed" \
|| (echo "Functions with high complexity found" && exit 1)
SonarQube ile Java için:
tatlım
# SonarQube quality gate blocks merge if CC exceeds threshold
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: |
-Dsonar.qualitygate.wait=true
-Dsonar.java.complexity.Function.threshold=10
Kalite kontrol noktası, yüksek karmaşıklığa sahip yeni kodlar üzerinde engellenmeli , mevcut eski kod tabanının tamamı üzerinde değil; çünkü mevcut kod tabanında zaten ayrı olarak ele alınan yüksek karmaşıklık sorunları olabilir.
COBOL ve Eski Kod Tabanlarında Döngüsel Karmaşıklık
Kurumsal sistemlerde, COBOL programlarında 50 hatta 100'ü aşan CC değerlerine sahip paragraflar bulunabiliyorsa, döngüsel karmaşıklık analizi modern kod tabanlarına göre farklı bir temel amaca hizmet eder. Soru "bu fonksiyonu yeniden düzenlemeli miyiz?" değil, "bu programın geçiş riski nedir ve hangi sırayla modernize etmeliyiz?" şeklindedir.
Ana paragrafında CC = 80 olan bir COBOL programının, her biri doğrulanması için bir test senaryosu gerektiren 80 bağımsız yürütme yolu vardır. Programda test senaryoları yoksa (ki çoğu eski COBOL programında bu durum söz konusudur), CC puanı, herhangi bir dönüştürme çalışmasının güvenli kabul edilebilmesi için kaç doğrulama senaryosunun oluşturulması gerektiğinin birincil göstergesidir.
SMART TS XL'S statik kod analizi COBOL, JCL, RPG, PL/I ve tüm modern diller için döngüsel karmaşıklığı eş zamanlı olarak hesaplar ve modernizasyon sıralama kararlarını kanıta dayalı hale getiren portföy düzeyinde CC dağılımını üretir. En yüksek CC puanlarına ve en çok çağıran programa (yüksek giriş sayısı) sahip programlar, bağlamda açıklandığı gibi en yüksek riskli geçiş hedefleridir. COBOL uygulamaları için sürdürülebilirlik endeksi ölçütlerinin oluşturulmasıCC, Halstead Volume ve kod satırlarını da içeren daha geniş bir kalite tablosunun yalnızca bir bileşenidir.
Etki analizi özelliği, yüksek karmaşıklığa sahip herhangi bir programda değişiklik yapıldığında neyin doğrulanması gerektiğini belirlemek için CC tabanlı karmaşıklık sınıflandırmasını kullanır: daha yüksek CC, daha fazla yürütme yolu anlamına gelir; bu da herhangi bir değişiklikten önce ve sonra davranışsal eşdeğerliği doğrulamak için daha fazla test senaryosunun doğrulanması gerektiği anlamına gelir.
Eski sistemlerin modernizasyon programlarını planlayan ekipler için , portföy genelindeki CC dağılımı, geçiş dalgası sıralamasının girdisidir: az sayıda arayanı olan düşük CC'li programlar erken geçiş yapar; çok sayıda arayanı olan yüksek CC'li programlar ise, ekip daha basit bileşenler üzerinde uzmanlık geliştirdikten ve karmaşık olanları doğrulamak için test altyapısı kurulduktan sonra en son geçiş yapar.
Karmaşıklık Düşman Değil, Görünmez Karmaşıklık Düşmandır
Döngüsel karmaşıklık, test edilebilirliğe doğrudan bağlanan az sayıdaki kod kalitesi ölçütünden biridir; tam yol kapsamı için gereken test senaryosu sayısı, tanımı gereği, en az döngüsel karmaşıklık puanına eşittir. Bu bağlantı, birçok kalite ölçütünün aksine, onu pratik olarak uygulanabilir hale getirir.
CC yönetiminin disiplini, karmaşıklığın kademeli olarak biriktiğini anlamayı gerektirir. if Büyüyen bir fonksiyona eklenen ifade, bireysel olarak makuldür. Üç yıllık bireysel olarak makul kararların sonucu, kimsenin dokunmak istemediği, çünkü gerçekten de güvenli bir şekilde akıl yürütmek için çok karmaşık olan CC = 40'lık bir fonksiyon olabilir. Bu kılavuzdaki araçlar ve teknikler (koruma maddeleri, metot çıkarımı, polimorfizm, koşullu ayrıştırma ve CI/CD kalite kapıları), bu birikimin görünmez bir şekilde gerçekleşmesini önlemek ve zaten gerçekleşmişse sistematik olarak ele almak için mevcuttur.