Stellarion
Quality

Cyclomatic Complexity

Cyclomatic complexity (CC) measures the number of linearly independent paths through a program's source code. Developed by Thomas McCabe in 1976, it remains one of the most widely used metrics for assessing code testability and maintainability.

What It Measures

Cyclomatic complexity quantifies structural complexity by counting decision points in your code. Each decision point creates a new execution path that potentially needs testing.

The core principle: more decision points = more paths to test = higher complexity.

How It's Calculated

Stellarion calculates cyclomatic complexity using AST (Abstract Syntax Tree) traversal, counting:

ConstructIncrement
if statement+1
else if+1
for loop+1
while loop+1
do-while loop+1
switch case+1 per case
catch block+1
&& operator+1
`
Ternary ?:+1

The base complexity is 1 (representing the default path through the function).

Formula: CC = (decision points) + 1

Or using graph theory: CC = E - N + 2P where E = edges, N = nodes, P = connected components.

Severity Classification

Stellarion classifies cyclomatic complexity using these thresholds:

ScoreSeverityColorAssessment
1-10LowGreenSimple, testable, maintainable
11-15ModerateYellowAcceptable complexity
16-20HighOrangeConsider refactoring
21-30HighOrangeRefactoring recommended
>30CriticalRedImmediate refactoring required

Why It Matters

Testing Implications

Cyclomatic complexity directly correlates with minimum test cases needed. For a function with CC=15, you need at least 15 test cases for full path coverage.

Stellarion recommendation: Create CC × 1.5 test cases to ensure adequate coverage including edge cases.

Maintainability Impact

Functions with high CC are:

  • Harder to understand and modify
  • More prone to introducing bugs during changes
  • Difficult to test comprehensively
  • Often violating the Single Responsibility Principle

Code Review Efficiency

High CC functions require more review time and are more likely to have defects slip through review.

How to Reduce Cyclomatic Complexity

1. Extract Methods

Break large functions into smaller, focused units:

# Before (CC=12)
def process_order(order):
    if order.is_valid:
        if order.has_items:
            for item in order.items:
                if item.in_stock:
                    # ... 20 more lines
                else:
                    # ... handle out of stock

# After (CC=3 each)
def process_order(order):
    if not order.is_valid:
        return handle_invalid_order(order)
    if not order.has_items:
        return handle_empty_order(order)
    return process_items(order.items)

2. Replace Conditionals with Polymorphism

Use the Strategy pattern or lookup tables:

// Before (CC=5)
function getDiscount(type) {
  if (type === 'gold') return 0.2;
  if (type === 'silver') return 0.1;
  if (type === 'bronze') return 0.05;
  return 0;
}

// After (CC=1)
const discounts = { gold: 0.2, silver: 0.1, bronze: 0.05 };
function getDiscount(type) {
  return discounts[type] || 0;
}

3. Simplify Boolean Expressions

Apply De Morgan's laws and extract named conditions.

4. Use Guard Clauses

Replace nested conditions with early returns.

Relationship to Test Coverage

Stellarion recommends maintaining test coverage proportional to complexity:

  • Coverage target: CC × 5% with minimum 80%
  • A function with CC=20 should have near 100% coverage
  • Low CC functions (1-5) can meet the 80% minimum

For the complete metrics reference, see Stellarion Quality Metrics.