F8. Cognitive Load Reduction
Diagnoses: D8. Working Memory Overflow Related fixes:
- F2 (Data Flow Primacy) — named pipeline stages are rest stops for working memory
- F9 (Type-Centric Modularization) — types act as chunks; one type’s operations is one mental unit
The concept
Section titled “The concept”(John Sweller, 1988; applied to software by many since)
Cognitive Load Theory distinguishes three types of load:
- Intrinsic load: the inherent complexity of the problem. You can’t reduce this.
- Extraneous load: complexity introduced by how the code is organized. This is the enemy. Scattered control flow, implicit conventions, unnecessary indirection, poor naming — all extraneous load.
- Germane load: the effort spent building useful mental models (understanding the domain, the architecture, the design decisions). This is productive work — you want to maximize it.
The goal is to minimize extraneous load so that the reader’s working memory is free for germane load — actually understanding what the code does and why.
Common sources of extraneous cognitive load
Section titled “Common sources of extraneous cognitive load”- Nested control flow. Each level of
if/else,try/catch, or loop adds a branch the reader must track. Deeply nested code forces the reader to maintain a mental stack. - Mixed iterations. A single loop or pipeline processes multiple unrelated pieces of data. The reader must track which transformation applies to which data.
- Action at a distance. A function modifies state that another distant function reads. The reader must hold both locations in mind simultaneously.
- Implicit conventions. The code relies on naming conventions, file placement rules, or framework magic that the reader must know to understand what happens.
- Dispersed branching. A single logical decision is split across multiple locations — part of the condition checked here, part checked there, the else case somewhere else entirely.
Remedy
Section titled “Remedy”- Flatten control flow. Use early returns, guard clauses, and pipeline-style transformations to reduce nesting. Each function should have one level of primary logic.
- One concern per iteration. If a loop or pipeline handles multiple unrelated transformations, split it into separate passes. Clarity beats micro-optimization.
- Use named pipeline stages as rest stops. When a value undergoes multi-step transformation, structure it as a pipeline with named, typed intermediates (see
F2-data-flow-pipelines.md). Each named intermediate lets the reader put down what they were holding and pick up the next stage fresh — working memory is freed between stages instead of accumulating across them. - Colocate decisions. If a branch has an if-path and an else-path, they should be visible in the same place. Don’t scatter the two sides of a decision across distant modules.
- Replace implicit conventions with explicit structure. If something must be known to understand the code, make it visible in the code — through types, through function signatures, through module structure.