Cyclomatic Complexity
Counts McCabe's decision points to estimate how many independent execution paths a file has.
McCabe's cyclomatic complexity (1976) counts every branching decision point — if, else if, for, while, case, &&, ||, ??, and catch each add 1 to the score. A baseline of 1 is added for the file itself.
Industry guidance from the SEI (Software Engineering Institute) correlates scores above 10 with elevated defect rates and recommends unit testing all independent paths. Vipr classifies scores up to 5 as excellent, 6–10 as good, 11–20 as fair, 21–30 as poor, and above 30 as critical.
Severity guide
- info
- Moderate complexity (6–10). Not alarming on its own, but worth checking whether some branches can be consolidated before they multiply.
- warning
- High complexity (above 10). The number of independent paths is high enough that test coverage and maintenance cost are likely growing faster than the code's value.
- critical
- Very high complexity (above 30). Branching has reached a point where the code is materially harder to change safely; refactor before the next round of changes.
Examples
Before
function classify(user) {
if (user.role === "admin" && user.active && !user.suspended) {
if (user.permissions.includes("write")) return "editor";
if (user.permissions.includes("read")) return "viewer";
return "admin-no-perms";
} else if (user.role === "guest") {
return user.invited ? "guest" : "anonymous";
}
return "unknown";
}After
function classify(user) {
if (user.role === "guest") return classifyGuest(user);
if (user.role !== "admin" || !user.active || user.suspended) return "unknown";
return classifyAdmin(user);
}
function classifyAdmin(user) {
if (user.permissions.includes("write")) return "editor";
if (user.permissions.includes("read")) return "viewer";
return "admin-no-perms";
}
function classifyGuest(user) {
return user.invited ? "guest" : "anonymous";
}Extracting cohesive branches into named helper functions reduces the cyclomatic complexity of each function. The same logic runs, but no single function has to be tested across the full Cartesian product of conditions.
Remediation
Extract cohesive branches into helper functions and replace nested conditionals with early returns or strategy lookups.
- Identify the largest branching cluster (often a multi-condition
if/elseladder) and extract its body into a named helper. - Convert nested
ifchains into early-return guard clauses where the early branches handle the simple cases. - Replace large
switchblocks orif/else ifladders with lookup objects (Map / Record) when the cases are data-driven. - Run the existing test suite after each extraction to confirm behavior is unchanged.