Skip to main content

Core analyzer

Cyclomatic Complexity

Counts McCabe's decision points to estimate how many independent execution paths a file has.

technical-debt core-cyclomatic

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/else ladder) and extract its body into a named helper.
  • Convert nested if chains into early-return guard clauses where the early branches handle the simple cases.
  • Replace large switch blocks or if/else if ladders with lookup objects (Map / Record) when the cases are data-driven.
  • Run the existing test suite after each extraction to confirm behavior is unchanged.

References

Documentation