Cognitive Complexity
SonarSource's cognitive complexity metric — penalizes deeply nested control flow more than cyclomatic complexity does.
Cognitive complexity, introduced by SonarSource, addresses cyclomatic complexity's main shortcoming: it treats all branches as equally costly. In reality, a flat switch with 10 cases is easier to read than a triply-nested if ladder with the same number of branches.
The metric adds 1 for each control-flow statement (if, for, while, switch, catch) and applies a nesting penalty that grows with depth. Logical operator chains and break-with-label statements also contribute. The result correlates more closely with how developers actually rate code readability than cyclomatic complexity does.
Severity guide
- info
- Complexity is within excellent or good range. No action required.
- warning
- Complexity exceeds warning threshold or nesting depth exceeds 3. Refactoring is recommended to keep the file readable as it grows.
- critical
- Complexity exceeds critical threshold. Most developers will struggle to read this file without extensive study.
Examples
Before
function process(items) {
if (items) {
if (items.length > 0) {
for (const item of items) {
if (item.active) {
if (item.type === 'a') {
// do work
}
}
}
}
}
}After
function process(items) {
if (!items || items.length === 0) return;
for (const item of items) {
if (!item.active || item.type !== 'a') continue;
doWork(item);
}
}Guard clauses (early returns and `continue` statements) flatten the nesting hierarchy without changing behavior. Cognitive complexity drops from 9 to 3.
Remediation
Reduce nesting via guard clauses and early returns; extract deeply nested blocks into named helpers.
- Identify the deepest nesting point and rewrite the outer condition to return or
continueearly instead of nesting the happy path. - Extract the body of any deeply nested loop or conditional into a named function — the function name documents the intent, and the call site loses an indent level.
- Break long boolean chains (
x && y && z && (a || b)) into named predicates that read like sentences. - Avoid nesting
try/catchblocks unless the inner catch handles something meaningfully different from the outer one.