cognitive-complexity
ESLint rule documentation for cognitive-complexity
📡 Live from GitHub — This documentation is fetched directly from cognitive-complexity.md and cached for 6 hours.
Keywords: cognitive complexity, code complexity, SonarQube, ESLint rule, code maintainability, refactoring, code quality, auto-fix, LLM-optimized
Enforces a maximum cognitive complexity threshold with refactoring guidance
Enforces a maximum cognitive complexity threshold with refactoring guidance. This rule is part of eslint-plugin-maintainability and provides LLM-optimized error messages with fix suggestions.
💡 Provides suggestions | ⚠️ Set to warn in recommended
Quick Summary
| Aspect | Details |
|---|---|
| Severity | Warning (code quality) |
| Auto-Fix | ⚠️ Suggests fixes (manual application) |
| Category | Quality |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | All projects, especially large codebases requiring maintainability |
Rule Details
Cognitive complexity is a measure of how difficult code is to understand. Unlike cyclomatic complexity, cognitive complexity takes into account nested structures and breaks that interrupt the linear flow of code.
Complexity Factors
| Factor | Weight | Example |
|---|---|---|
| Conditionals | +1 | if, else if, ? :, ?? |
| Loops | +1 | for, while, do-while |
| Switches | +1 per case | switch statement cases |
| Nesting | +nesting level | Nested if inside for |
| Logical Operators | +1 | &&, || (sequence breaks) |
| Catch blocks | +1 | catch in try-catch |
| Recursion | +1 | Function calling itself |
Error Message Format
The rule provides LLM-optimized error messages that include actionable guidance:
⚡ Cognitive Complexity: 23/15 (8 over) | Function: processOrder(order) | src/orders.ts:45
📊 Breakdown: 7 conditionals, 3 loops, 4 max nesting
💡 Recommended Pattern: Extract Method + Guard Clauses
🔧 Refactoring Steps:
1. Extract nested blocks into helper functions
2. Replace nested if/else with guard clauses (early returns)
3. Apply Extract Method + Guard Clauses to reduce branching logic
4. Target complexity: 15 or lower
⏱️ Estimated effort: 24 minutesMessage Components
| Component | Purpose | Example |
|---|---|---|
| Complexity Score | Shows current/max and overage | 23/15 (8 over) |
| Function Name | Identifies problematic function | processOrder(order) |
| Location | File path and line number | src/orders.ts:45 |
| Breakdown | Detailed complexity factors | 7 conditionals, 3 loops, 4 max nesting |
| Pattern | Recommended refactoring pattern | Extract Method + Guard Clauses |
| Steps | Concrete refactoring actions | Numbered list of steps |
| Effort | Time estimate for refactoring | 24 minutes |
This format is optimized for:
- 🤖 LLMs - Can parse and act on the structured guidance
- 👨💻 Developers - Clear, actionable information
- 📊 Planning - Time estimates for task estimation
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
maxComplexity | number | 15 | Maximum allowed cognitive complexity |
includeMetrics | boolean | true | Include detailed complexity breakdown in messages |
Examples
❌ Incorrect
// Cognitive complexity: 23 (too high!)
function processOrder(order: Order) {
if (order.status === 'pending') {
// +1
if (order.items.length > 0) {
// +2 (nested)
for (const item of order.items) {
// +3 (nested)
if (item.quantity > 0) {
// +4 (nested)
if (item.price > 100) {
// +5 (nested)
if (item.discount && item.discount > 0) {
// +6 (nested) +1 (&&)
// Process high-value discounted item
} else {
// +1
// Process high-value regular item
}
} else {
// +1
// Process low-value item
}
}
}
} else {
// +1
throw new Error('Empty order');
}
} else if (order.status === 'cancelled') {
// +1
// Handle cancellation
}
}✅ Correct
// Cognitive complexity: 5 (much better!)
function processOrder(order: Order) {
validateOrder(order); // +0 (extracted)
if (order.status === 'pending') {
// +1
processItems(order.items);
} else if (order.status === 'cancelled') {
// +1
handleCancellation(order);
}
}
function validateOrder(order: Order) {
// Complexity: 2
if (order.items.length === 0) {
// +1
throw new Error('Empty order');
}
}
function processItems(items: OrderItem[]) {
// Complexity: 4
for (const item of items) {
// +1
if (item.quantity > 0) {
// +2 (nested)
processValidItem(item);
}
}
}
function processValidItem(item: OrderItem) {
// Complexity: 3
if (item.price > 100) {
// +1
processHighValueItem(item);
} else {
// +1
processLowValueItem(item);
}
}Configuration Examples
Basic Usage
{
rules: {
'maintainability/cognitive-complexity': ['warn', {
maxComplexity: 15
}]
}
}Strict Mode
{
rules: {
'maintainability/cognitive-complexity': ['error', {
maxComplexity: 10,
includeMetrics: true
}]
}
}Relaxed Mode (Legacy Code)
{
rules: {
'maintainability/cognitive-complexity': ['warn', {
maxComplexity: 25,
includeMetrics: false // Less verbose for gradual refactoring
}]
}
}Refactoring Strategies
1. Extract Method
// Before: Complexity 18
function validateUser(user: User) {
if (user.email) {
if (user.email.includes('@')) {
if (user.email.length > 5) {
// More validation...
}
}
}
}
// After: Complexity 5
function validateUser(user: User) {
if (user.email) {
validateEmail(user.email);
}
}
function validateEmail(email: string) {
if (!email.includes('@')) return false;
if (email.length <= 5) return false;
return true;
}2. Use Guard Clauses
// Before: Complexity 12
function processPayment(payment: Payment) {
if (payment.amount > 0) {
if (payment.method === 'credit') {
if (payment.card) {
// Process credit card
}
}
}
}
// After: Complexity 3
function processPayment(payment: Payment) {
if (payment.amount <= 0) return;
if (payment.method !== 'credit') return;
if (!payment.card) return;
// Process credit card
}3. Strategy Pattern
// Before: Complexity 15
function calculateShipping(order: Order) {
if (order.type === 'express') {
if (order.weight > 10) {
return order.weight * 5;
} else {
return 20;
}
} else if (order.type === 'standard') {
// More conditions...
}
}
// After: Complexity 2
const shippingStrategies = {
express: (order: Order) => order.weight > 10 ? order.weight * 5 : 20,
standard: (order: Order) => /* ... */,
};
function calculateShipping(order: Order) {
const strategy = shippingStrategies[order.type];
return strategy ? strategy(order) : 0;
}Why This Matters
| Issue | Impact | Solution |
|---|---|---|
| 🧠 Maintainability | Hard to understand and modify | Extract methods |
| 🐛 Bug Density | More bugs in complex functions | Simplify logic |
| ⏱️ Review Time | Takes longer to review complex code | Break into smaller pieces |
| 📝 Testing | Harder to write comprehensive tests | Reduce branching |
| 🔄 Refactoring | Risky to change complex functions | Use design patterns |
Comparison with Cyclomatic Complexity
| Aspect | Cognitive Complexity | Cyclomatic Complexity |
|---|---|---|
| Nesting Impact | ✅ Penalizes deeply nested | ❌ Treats all equal |
| Linear Flow | ✅ Recognizes clarity | ❌ Ignores flow |
| Readability Focus | ✅ Human-centric | ❌ Branch-centric |
| Better for | Understanding code | Test coverage |
Comparison with Alternatives
| Feature | cognitive-complexity | eslint-plugin-complexity | SonarQube |
|---|---|---|---|
| Cognitive Complexity | ✅ Yes | ❌ Cyclomatic only | ✅ Yes |
| LLM-Optimized | ✅ Yes | ❌ No | ❌ No |
| ESLint MCP | ✅ Optimized | ❌ No | ❌ No |
| Fix Suggestions | ✅ Detailed | ⚠️ Basic | ⚠️ Basic |
| ESLint Integration | ✅ Native | ✅ Native | ❌ External |
Related Rules
identical-functions- Detects duplicate codeno-circular-dependencies- Prevents circular importsno-internal-modules- Enforces module boundaries
Further Reading
- Cognitive Complexity - SonarSource - Cognitive complexity white paper
- G. Ann Campbell: Cognitive Complexity - Original cognitive complexity paper
- SonarQube RSPEC-3776 - SonarQube complexity rule
- ESLint MCP Setup - Enable AI assistant integration
References
Inspired by SonarQube RSPEC-3776
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Dynamic Variable References
Why: Static analysis cannot trace values stored in variables or passed through function parameters.
// ❌ NOT DETECTED - Value from variable
const value = externalSource();
processValue(value); // Variable origin not trackedMitigation: Implement runtime validation and review code manually. Consider using TypeScript branded types for validated inputs.
Imported Values
Why: When values come from imports, the rule cannot analyze their origin or construction.
// ❌ NOT DETECTED - Value from import
import { getValue } from './helpers';
processValue(getValue()); // Cross-file not trackedMitigation: Ensure imported values follow the same constraints. Use TypeScript for type safety.