name: cognitive-load-reviewer description: Analyze and reduce cognitive load in code by identifying complex conditionals, deep nesting, shallow modules, excessive abstractions, and architectural over-engineering. Use when reviewing code quality, refactoring for maintainability, or simplifying systems. Keywords: cognitive load, complexity, refactor, simplify, maintainability, readability, mental overhead, abstraction, architecture review
Cognitive Load Reviewer
Identifies and reduces extraneous cognitive load in software systems following evidence-based principles.
Core Principle
Working Memory Limit: Humans can hold ~4 chunks of information simultaneously. Code exceeding this threshold creates mental fatigue and slows development.
Goal: Reduce extraneous cognitive load (caused by poor design) while accepting intrinsic load (inherent domain complexity).
Analysis Framework
1. Complex Conditionals
Look for:
- Nested boolean expressions:
if val > X && (a || b) && (c && !d) - Multiple conditions without intermediate variables
- Logic requiring mental simulation to understand
Fix:
// ❌ High cognitive load
if val > constant && (cond2 || cond3) && (cond4 && !cond5) {
// ...
}
// ✅ Externalized mental bookkeeping
let exceeds_threshold = val > constant;
let has_valid_mode = cond2 || cond3;
let is_enabled = cond4 && !cond5;
if exceeds_threshold && has_valid_mode && is_enabled {
// ...
}
2. Deep Nesting
Look for:
- Multiple levels of indentation (>3 levels)
- Nested if/match statements
- Accumulated context requirements
Fix:
// ❌ Nested context tracking
fn process(data: Option<Data>) -> Result<Output> {
if let Some(data) = data {
if data.is_valid() {
if let Some(result) = data.compute() {
return Ok(result);
}
}
}
Err(Error::Invalid)
}
// ✅ Early returns focus on happy path
fn process(data: Option<Data>) -> Result<Output> {
let data = data.ok_or(Error::Missing)?;
if !data.is_valid() {
return Err(Error::Invalid);
}
data.compute().ok_or(Error::ComputeFailed)
}
3. Shallow vs. Deep Modules
Identify shallow modules:
- Complex interface with minimal functionality
- Many small functions/methods requiring multiple mental models
- High coupling between components
Prefer deep modules:
- Simple interface hiding complex implementation
- Unix I/O principle: 5 basic calls, 100K+ lines internal complexity
- Single entry point with clear contract
Example:
// ❌ Shallow - many small pieces
struct UserValidator;
struct PasswordHasher;
struct SessionCreator;
struct TokenGenerator;
// ... requires understanding 4+ components
// ✅ Deep - simple interface, complex internals
struct AuthService {
pub fn authenticate(&self, credentials: Credentials) -> Result<Session>
// Hides validation, hashing, session creation internally
}
4. Abstraction Layers
Red flags:
- Hexagonal/Onion architecture with 5+ layers
- Repository patterns abstracting simple queries
- Domain/Application/Infrastructure separation without clear benefit
- "MetricsProviderFactoryFactory" naming patterns
Reality check:
- Can newcomers contribute within hours or days?
- Does debugging require tracing through multiple layers?
- Are abstraction layers solving real extension points or architectural purity?
Principle: Add abstraction only for justified extension points, not preemptive flexibility.
5. DRY Abuse
Look for:
- Shared code between unrelated domains
- Premature abstractions creating tight coupling
- "Generic" utilities requiring mental mapping
Guideline:
// ✅ "A little copying is better than a little dependency"
// Duplicate 5-10 lines if it maintains clarity and independence
// Abstract only when 3+ use cases demonstrate clear pattern
6. Microservices Over-Granularity
Warning signs:
- Changes require modifying 4+ services
- Distributed monolith patterns
- More services than team members
Case study: 5 developers, 17 microservices → 10 months behind schedule
Principle: Defer network boundaries until logical system boundaries are clear.
7. Framework Magic
Look for:
- Business logic embedded in framework code
- Annotations/macros hiding control flow
- "Magic" requiring framework internals knowledge before contributing
Fix:
// ❌ Framework-coupled business logic
#[framework::magic_handler]
async fn process_order(ctx: FrameworkContext) -> FrameworkResponse {
// Business logic intertwined with framework
}
// ✅ Framework as library, business logic separate
async fn process_order(order: Order) -> Result<Receipt> {
// Pure business logic, framework-agnostic
}
// Adapter layer handles framework integration
8. Self-Describing Values
Look for:
- Numeric error codes requiring mental mappings
- Boolean flags without semantic names
- Magic numbers without constants
Fix:
// ❌ Requires memorization
return Err(401);
// ✅ Self-describing
return Err(AuthError::JwtExpired);
// ❌ Magic number
if status == 418 { ... }
// ✅ Named constant
const TEAPOT_STATUS: u16 = 418;
if status == TEAPOT_STATUS { ... }
9. Inheritance Hierarchies
Look for:
- Multiple inheritance levels (>2)
- Vertical cognitive chains requiring class-hopping
- Overridden behavior modifications
Prefer:
- Composition over inheritance
- Trait/interface implementations without deep hierarchies
- Flat structures with explicit dependencies
Analysis Process
Step 1: Identify Cognitive Hotspots
Scan code for:
- Functions >50 lines
- Nesting depth >3 levels
- Complex boolean logic
- Multiple abstraction layers
- Module coupling patterns
Step 2: Measure Mental Overhead
Ask:
- How many facts must I hold in memory?
- Can I understand this without context-switching?
- Would a newcomer understand this in <5 minutes?
- Does this require memorizing mappings/conventions?
Step 3: Categorize Load Type
- Intrinsic: Domain complexity (accept it)
- Extraneous: Design choices (fix it)
Step 4: Recommend Improvements
Prioritize:
- High impact, low effort: Extract intermediate variables, early returns
- Medium impact: Simplify module boundaries, reduce abstractions
- Strategic: Architecture simplification, framework decoupling
Output Format
Present findings as:
🧠 Cognitive Load Analysis
High Load Areas (>4 mental chunks required)
crates/parser/src/query.rs:145- Complex conditional with 6 boolean clausescrates/storage/src/engine.rs:89- 5 levels of nesting, difficult to track state
Shallow Module Patterns (complex interface, minimal functionality)
StorageAdapter+QueryExecutor+ResultMappercould merge into singleQueryEngine
Excessive Abstraction
- Repository pattern adds indirection without extension points
- Consider direct SQL calls via jOOQ-style query builder
Improvement Priorities
Extract intermediate variables in
query.rs:145let has_valid_columns = !columns.is_empty() && columns.iter().all(|c| c.is_valid()); let within_bounds = offset >= 0 && limit <= MAX_LIMIT; if has_valid_columns && within_bounds { ... }Flatten nesting in
engine.rs:89using early returnsMerge shallow modules to create deeper interface with simpler mental model
Onboarding Metric
Critical test: Can a new developer contribute within 1-2 days?
If newcomers struggle >40 consecutive minutes, cognitive load needs reduction.
Project Integration
Reference CLAUDE.md principles:
- Incremental development reduces cognitive overhead
- TDD externalizes requirements into tests
- Small commits maintain reviewability
- Parallel sub-agents distribute analysis cognitive load
Success Examples
- Instagram: 14M users with 3 engineers (simple architecture)
- Unix I/O: Deep module pattern (5 calls, 100K+ lines hidden)
- Raft consensus: Designed explicitly for understandability
Anti-Pattern Summary
❌ Overly granular module decomposition ❌ Excessive architectural layers without benefit ❌ Numeric codes requiring memorization ❌ Framework-embedded business logic ❌ Premature DRY abstractions ❌ Deep inheritance chains ❌ Nested conditionals without intermediate variables ❌ Shallow modules forcing multi-component mental models
Remember
"The best components provide powerful functionality yet have a simple interface."
Optimize for reducing the mental effort required to understand and modify code.