Complexity¶
Three rules measure how complicated a function's control flow is. They share one implementation for every language, so a value of 7 means the same thing in TypeScript, Python, Java and Go.
| Rule | ID | Default | Measures |
|---|---|---|---|
| Cyclomatic complexity | complexity.cyclomatic |
12 | How many paths run through the function |
| Cognitive complexity | complexity.cognitive |
15 | How hard the function is for a person to follow |
| Nesting depth | complexity.nesting_depth |
4 | How deeply its conditions and loops are nested |
Worked example¶
The numbers below are the tool's real output for this function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Cyclomatic complexity 7, cognitive complexity 7, nesting depth 3.
Cyclomatic complexity¶
The number of independent paths through a function. Each path is something to understand and to test.
How it counts. Start at 1, then add 1 for each:
if,else iforelif;- loop;
catchorexcepthandler;caseother thandefault(in Go,case 1, 2:is one case);- conditional expression (
a ? b : c); &&or||(and,orin Python; also??in JavaScript and TypeScript);- in Python, each
forandifinside a comprehension.
In the example: 1, plus the if on line 5, the loop on line 9, the if and its && on line 10,
the else if on line 12, and the conditional on line 16. Branches inside a nested function do not add
to the outer function.
Note
Tools disagree about counting && and ||. Do not compare these numbers with another tool's.
Cognitive complexity¶
A score for how hard the code is to read. It charges for every break in straight-line reading, and charges more when that break is nested.
How it counts:
- +1 for
if,else if,else, a conditional expression,switchormatch, each loop, eachcatchorexcept, and eachbreakorcontinueto a label. - +1 for each level of nesting on
if, conditional expressions,switch, loops andcatch.else ifandelsenever get this extra charge. - +1 for each run of the same logical operator:
a && b && cis one run;a && b || cis two. - A nested function raises the nesting level for its contents, and its points count toward the outer function too.
In the example:
| Line | Construct | Points |
|---|---|---|
| 5 | if |
1 |
| 9 | for loop |
1 |
| 10 | if, nested inside the loop |
1 + 1 |
| 10 | && run |
1 |
| 12 | else if |
1 |
| 16 | conditional expression | 1 |
| Total | 7 |
The JSON report lists every point with its line. This is an independent implementation of the
Cognitive Complexity specification published by SonarSource (version 1.5), with no SonarSource code. It
was checked against the specification's examples, not against SonarQube, so edge cases can differ.
Recursion, ?? and Python comprehensions are not counted.
Nesting depth¶
The deepest level of nesting in a function. if, loops, switch or match, catch or except, and
conditional expressions each add a level, and else if counts one level deeper than its if. Nested
functions start again from zero, and Python comprehensions do not count.
In the example: the loop is level 1, the if level 2, and the else if level 3. Tools that treat
else if as flat report lower numbers.
How to lower it¶
- Return early. Handle invalid or trivial cases first, instead of wrapping the main path in an
if. - Replace chains of
iforswitchwith a lookup table when they map a value to a result. - Move a loop body into its own function, so the loop reads as one line.
- Name parts of long conditions:
const eligible = isActive && hasBalance;. - Split a function that does several jobs in a row, such as validate, calculate, save and notify, and keep the original as a short coordinator.
Start with the lines that carry the deepest nesting. The JSON report's evidence shows them.
Language notes¶
| Construct | JavaScript and TypeScript | Python | Java | Go |
|---|---|---|---|---|
| Else-if | else if |
elif |
else if |
else if |
| Conditional | a ? b : c |
b if a else c |
a ? b : c |
none |
| Switch | switch, case, default |
match, case, case _: as default |
switch statements and expressions; each case label counts |
switch, type switch and select; each clause counts once, default never |
| Loop | for, for-in, for-of, while, do-while |
for, async for, while; a loop's else adds 1 cognitive |
for, enhanced for, while, do-while |
for, for range |
| Catch | catch |
each except or except* |
each catch; a multi-catch is one |
none |
| Labelled jump | break label, continue label |
none | break label, continue label |
break label, continue label, goto |
| Logical operators | &&, \|\|, and ?? for cyclomatic only |
and, or |
&&, \|\| |
&&, \|\| |
Python f-strings are read as one piece, so conditions inside {...} are not counted. In Go, a
function literal is measured on its own and also raises the nesting of the function that holds it, like
a nested function in any other language.