Also called McCabe complexity, CCN, Cyclomatic complexity number
In depth Nested vs cyclomatic complexity: the same code scored both ways, and which metric to trust for what.
How cyclomatic complexity is measured
McCabe's 1976 paper, "A Complexity Measure" (IEEE Transactions on Software Engineering), defines it on the control-flow graph: M = E - N + 2P. For one function, P = 1. A function with a single if has three nodes (the condition, the branch body, the exit) and three edges, so M = 3 - 3 + 2 = 2.
In practice: start at 1, add 1 for each if, elif, loop, case, catch, ternary, and each && or ||.
A worked example:
def ship(order):
if order.paid and order.items: # +1 if, +1 and
for item in order.items: # +1 loop
if item.fragile: # +1 if
pack_with_foam(item)
else:
pack(item)
elif order.draft: # +1 elif
discard(order)Five decision points, so complexity is 6, also the number of independent paths to test.
Tools disagree on details such as how a switch is counted, so compare numbers only within one tool.
Why cyclomatic complexity matters
Each independent path needs a test, so complexity 25 means at least 25 cases, and more branches mean more states to hold in your head. The metric ignores layout: ten checks in a row and ten nested ten deep both score 11, which is why cognitive complexity exists. A complex function nobody changes is cheap to leave; the CRAP score combines complexity with coverage to find the ones that matter.
How to measure cyclomatic complexity yourself
- Python:
radon cc -s -a src/prints a letter grade and number per function;flake8 --max-complexity 10fails on anything above 10. - Many languages:
lizard src/reports CCN per function across C, C++, Java, JavaScript, Go, Python, Rust and others;lizard -C 10 -w src/prints only functions above 10. - JavaScript and TypeScript: the ESLint
complexityrule, for example"complexity": ["error", 10]. - Go:
gocyclo -over 10 ..
These suit a lint gate in CI. They do not know which functions change often.
How repowise measures cyclomatic complexity
repowise computes McCabe complexity (it calls it CCN) per function in a single tree-sitter pass, for languages with a complexity map, using the same rule above: branches, loops, cases, catches and boolean operators. else if and elif chains add CCN but not a nesting level. C and C++ #if/#else blocks take the largest arm, not the sum, because the arms are alternative builds.
Thresholds in the detectors, read from the source:
| Detector | Fires when | Severity steps |
|---|---|---|
complex_method | CCN 9 or higher | medium from 9, high from 15, critical from 25 |
brain_method | NLOC 70+, CCN 9+ and a central file | listed, not scored |
large_method | NLOC 60+ and CCN 3 or more | low to critical by length |
god_class | 200+ code lines, 15+ methods and a brain method | severity by size |
For a function that is mostly one switch or if chain on a single value, the detectors judge the code around the dispatch plus its heaviest arm, and the finding shows the raw count too. Complexity is read beside churn, so a CCN 47 function in a file changed 19 times this quarter outranks the same function in a quiet file.
Where to see it: repowise health (--file <path> for one file), get_health over MCP, and the code health feature page. No LLM is involved. For the paths-versus-reading-cost comparison, see nested vs cyclomatic complexity.