Cyclomatic Complexity and CRAP Metrics Guide
Learn how Cyclomatic Complexity and CRAP metrics evaluate code maintainability execution paths and change risk.
Overview
Cyclomatic Complexity and Change Risk Anti-Patterns (CRAP) are essential software engineering metrics used to evaluate code maintainability, testability, and modification risk. Cyclomatic Complexity measures execution paths within a function, while CRAP combines complexity with code coverage to quantify the risk of introducing defects during changes.
Key Insights
- Execution Path Tracking: Cyclomatic Complexity quantifies the number of independent execution paths in code, directly impacting test effort and readability.
- Exponential Risk Penalty: The CRAP score heavily penalizes complex logic that lacks automated tests, highlighting high-risk areas in pull requests and code reviews.
- Dual-Path Remediation: High CRAP scores can be lowered either by reducing code complexity through refactoring or by increasing test coverage.
- Target Thresholds: Maintain a Cyclomatic Complexity score below 10 and a CRAP score under 30 to ensure code stability.
Technical Details
Cyclomatic Complexity
Cyclomatic Complexity calculates structural complexity based on control flow branches. Every function starts with a baseline score of 1 and increases by 1 for each conditional branch or iteration construct.
Common complexity drivers include:
- Control Structures:
if,elseif,case,match - Loops:
for,foreach,while - Null-Coalescing & Operators:
??,?:, ternary expressions
Complexity Score Matrix
| Score Range | Complexity Level | Assessment |
|---|---|---|
| 1–6 | Low | Ideal; clean and easy to test. |
| 7–9 | Moderate | Acceptable; still manageable. |
| 10–20 | High | Difficult to maintain; split logic where possible. |
| 20+ | Very High | Critical risk; high defect probability and hard to test. |
Code Path Progression
Evaluating how conditional branching increments complexity:
// Cyclomatic Complexity: 1
function getUserStatus($user) {
return $user->status;
}
// Cyclomatic Complexity: 2 (1 conditional statement)
function getUserStatus($user) {
if ($user->isActive()) {
return 'active';
}
return 'inactive';
}
// Cyclomatic Complexity: 4 (3 conditional statements)
function getUserStatus($user) {
if (!$user->isActive()) {
return 'inactive';
}
if ($user->isPremium()) {
return 'premium';
}
if ($user->isTrial()) {
return 'trial';
}
return 'active';
}
Complexity Reduction Strategies
When complexity exceeds acceptable limits, apply the following design patterns:
- Extract Methods: Split large functions into smaller, single-responsibility methods.
- Guard Clauses: Use early returns to flatten nested conditional trees.
- Polymorphism: Replace conditional blocks (
switch/caseor largeif/elsechains) with polymorphic classes. - Lookup Tables: Map inputs directly to outputs using configuration arrays or dictionaries.
Note: Necessary business logic may justify moderate complexity, provided it is fully covered by automated tests.
Change Risk Anti-Patterns (CRAP)
The CRAP metric measures the risk of changing code by balancing Cyclomatic Complexity (CC) against code coverage.
Mathematical Formula
$$\text{CRAP} = \text{CC}^2 \times (1 - \text{coverage}/100)^3 + \text{CC}$$
The cubic exponent on uncovered code causes CRAP scores to rise exponentially when complex functions lack adequate unit tests.
Risk Thresholds
- 0–30: Low Risk (Acceptable)
- 30–60: Moderate Risk (Requires additional unit tests or targeted refactoring)
- 60+: High Risk (Prioritize for immediate refactoring and test implementation)