Also called Nested complexity, Understandability metric
How cognitive complexity is measured
SonarSource's rules reduce to three ideas:
- Ignore structure that makes code easier to read, such as extracting a function.
- Add 1 for each break in linear flow:
if,else if,else, ternary, loops,catch, aswitch(once, however many cases),goto, and each sequence of like boolean operators (a && b && cis +1,a && b || cis +2). - Add the current nesting level to a flow-breaking structure that sits inside another one.
Take the same four checks written two ways:
def enable(user, flag):
if user: # +1
if user.is_active: # +2 (nested 1 deep)
if user.plan == "pro": # +3
if flag: # +4
enable_feature() # cognitive 10, cyclomatic 5
def enable(user, flag):
if not user: return # +1
if not user.is_active: return # +1
if user.plan != "pro": return # +1
if not flag: return # +1
enable_feature() # cognitive 4, cyclomatic 5Both have cyclomatic complexity 5, because both have four decisions. Cognitive complexity drops from 10 to 4 because the checks no longer nest. The reverse also happens: a switch with 12 flat cases scores 13 cyclomatic and 1 cognitive, because a reader scans cases, not traces them.
Why cognitive complexity matters
Reading cost, not path count, is what slows reviews and causes mistakes in edits. Cognitive complexity gives that cost a number, so a refactor like the guard-clause rewrite above shows up as a score change, not an opinion. It agrees with cyclomatic complexity less than you would expect, which is the point: a function high on both is the strongest refactoring candidate, and one high on only one tells you which kind of problem it is. Campbell's white paper is the reference for the rules. Treat the number as a readability tool, not a bug predictor.
How to measure cognitive complexity yourself
- SonarQube, SonarCloud and SonarLint compute it natively; the default rule threshold is 15.
- ESLint: the
sonarjs/cognitive-complexityrule ineslint-plugin-sonarjs. - Go:
gocognit(gocognit -over 15 .). - Python:
flake8-cognitive-complexityadds a check to flake8, andcomplexipyreports it per function. - Rust: Clippy has a
cognitive_complexitylint (in the nursery group, off by default).
SonarSource's own tools are the reference implementation, so pick them if you want the published numbers exactly. The others are ports and can differ on edge cases.
How repowise measures cognitive complexity
repowise's walker computes a SonarSource-style cognitive score for every function in the same pass as cyclomatic complexity: a weighted nesting cost, with else if arms charged a flat +1 without opening a level, and a switch or match charged once with its case arms adding nothing. The score is stored and shown in a finding's details.
Reading cost is judged through several signals, all read from the source:
nested_complexityflags maximum nesting depth of 4 or more (high at 5, critical at 7). This is depth, not the cognitive total. It is a scored detector.complex_conditionalflags boolean expressions with 3 or more operators; a flat chain of one operator is not flagged.repeated_nestingflags several nested blocks side by side in one function (3 or more, with CCN 5 or more); it is listed, not scored.complex_methoduses cyclomatic complexity (9 or more) and reports the cognitive value next to it.
Files are then ranked with git history, so a deeply nested function in a file changed 19 times this quarter outranks the same function in a quiet one. See repowise health on the CLI, get_health over MCP, the code health feature page and the refactoring plans page, which proposes Extract Method for the first lift. All analysis is deterministic and uses no LLM. For the two metrics side by side, see nested vs cyclomatic complexity.