Skip to content

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

(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”
  1. 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.
  2. Mixed iterations. A single loop or pipeline processes multiple unrelated pieces of data. The reader must track which transformation applies to which data.
  3. Action at a distance. A function modifies state that another distant function reads. The reader must hold both locations in mind simultaneously.
  4. Implicit conventions. The code relies on naming conventions, file placement rules, or framework magic that the reader must know to understand what happens.
  5. 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.
  • 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.