Cognitive Load Is Your AI Tax
AI teams drown not because the model is weak, but because the system is mentally expensive.
Confusion shows up as delays, bugs, brittle demos, “we’ll fix it next sprint,” and half-shipped features. That confusion has a root cause: cognitive load.
We spend far more time reading, aligning, and debugging than writing code. In AI-native products, we also read data contracts, evaluation reports, prompt graphs, agent flows, feature flags, and executive dashboards. Each item rents space in the head. Working memory holds only a few chunks at once; exceed that and velocity collapses.
The job is not to be clever. The job is to reduce load.
Cognitive Load (for AI teams)
Definition. Cognitive load is how much a person must hold in mind to make a correct change.
When building AI systems, the “facts to hold” multiply quickly:
- Which dataset version trained this model?
- What post-processing rules apply to this route?
- Which prompt variant is live for segment X behind flag Y?
- What safety policy governs this surface and who owns exceptions?
- Where is the feedback loop persisted and how is it routed to re-training?
Most failures in AI orgs are not model failures — they are memory failures.
We’ll use this shorthand:
- 🧠 — fresh working memory (0–1 facts)
- 🧠++ — mild load (2–3 facts)
- 🤯 — overload (≥4 facts, context thrash begins)
Types of Cognitive Load
- Intrinsic load — the hard part that cannot be removed. ML is probabilistic; data shifts; evaluation is nuanced. Accept it.
- Extraneous load — the hard part we add unintentionally: cute abstractions, bespoke conventions, undocumented rituals, framework worship, cargo-cult architecture. Eliminate it relentlessly.
This article is about extraneous load.
Practical Sources of Extraneous Load (and how to kill them)
1) Prompt-Spaghetti and Hidden State
if user_segment == ‘enterprise’ and risk==‘low’ and intent in [‘how’,‘what’] and …
prompt = BASE_PROMPT + LEGAL_SUFFIX + REGION_FOOTER
By line three, we’re at 🧠+++.
Do instead: introduce named invariants and self-describing strategies.
isEnterprise = user.segment == ‘enterprise’ # 🧠
isLowRisk = risk == ‘low’ # 🧠
isHowWhat = intent in {‘how’,‘what’} # 🧠
strategy = select_prompt_strategy(isEnterprise, isLowRisk, isHowWhat) # 🧠
render(strategy).execute()
- Name the concept, not the condition.
- Keep the happy path flat; push branches to leaf decisions.
- Store assembled prompts as artifacts with IDs and metadata; never hide them in “just a function.”
Result: reviewers evaluate meaning, not string concatenation.
2) Agentic Over-Orchestration
Five agents call three tools each, passing partial memories around a shared black box. Diagrams are pretty; debugging is not. Every additional hop costs 🧠.
Rule: Prefer few deep agents over many shallow agents. Each deep agent has a clear contract, bounded memory, and observability hooks.
Smell: “We need an agent for that” became reflex.
Fix: Try a deterministic step first. Agentify only where uncertainty buys value (search, synthesis, judgment). Everything else: functions.
3) Inheritance in Systems Design
“BaseLLMEvaluator → PolicyAwareEvaluator → RiskTunedEvaluator → RegionSpecificEvaluator → …”
By the time you reach the leaf, you’ve mentally re-played four class hierarchies. 🤯
Prefer composition: Evaluator(base, policy, risk, region).
Swap capabilities explicitly; read behavior locally; test in isolation.
4) Shallow Modules, Everywhere
A forest of tiny wrappers — PromptLoader, PromptRenderer, PromptDecorator, PromptMux, PromptEnv. Each is ten lines. Together they require you to remember all their interactions.
Deep vs Shallow:
- Deep module: simple interface, hides real complexity, earns trust.
- Shallow module: complex interface for trivial work; leaks complexity.
Aim for deep: one Prompts module with three operations:
register(id, template, policy)
render(id, inputs) -> PromptArtifact
audit(id) -> Metadata
Keep the interface boring; bury the mess.
5) Microservices Too Soon
Seventeen services for a five-person team. Every change touches four repos and three CI pipelines. Integration diagnostics explode. 🤯
Heuristic: Start monolith, isolate domains internally, extract only when deployment independence becomes the bottleneck.
Monolith first reduces cognitive load: one timeline, one profiler, one schema migration. Strong seams let you carve out later without trauma.
6) Feature-Fetish Frameworks
“We adopted framework X; it gives us agents, routing, tools, memory, eval, UI, and coffee.” Wonderful — now the entire team must internalize the magic. Newcomers spend weeks learning indirection before shipping code. 🤯
Guideline: Treat frameworks as libraries, not as your home.
- Put business logic outside the framework shell.
- Wrap the couple of features you truly need.
- Keep exits clear.
7) DRY That Binds
A small shared “utils” to unify tokenization, scoring, logging, retries, serialization, tracing, and six more concerns. Now every module depends on it; every breaking change is a system-wide seminar. 🤯
Robust alternative: A little copying beats a little dependency.
Duplicate a 12-line retry wrapper per service if it saves a 12-repo migration on Friday night.
8) Ambiguous Status Codes and Telepathy Contracts
Backend returns 403, which sometimes means “expired token” and sometimes “insufficient permissions.” QA asks which. Frontend guesses. Everyone re-creates the original author’s mental mapping.
Do this:
{ “code”: “jwt_expired”, “hint”: “relogin”, “trace”: “abc123” }
- Self-describing codes, not numeric trivia.
- Hints for first action.
- Trace for observability.
Cognitive load shifts from remembering to reading.
9) Layering for its Own Sake
Hexagonal, onion, ports, adapters, rings, slices — every layer adds a hop and a concept. If each hop adds 🧠, stack enough and you get 🤯.
Keep one fundamental: dependency inversion.
- Business core depends on interfaces, not infra.
- Tests run without databases or clouds.
Everything else must earn its keep.
10) DDD Where Language Beats Folders
Ubiquitous language is gold; folder choreography is not. Ten engineers, ten interpretations of “bounded context,” fifteen diagrams later, nothing ships.
Use DDD for thinking (naming, boundaries, conversations).
Use Team Topologies to allocate cognitive load across teams.
Organize repos for flow, not for a poster.
The AI-Native Working-Memory Model
When an engineer touches a production path, they must temporarily hold:
- Data — dataset/version/features/owners.
- Model — artifact ID/config/metrics/eval caveats.
- Policy — guardrails/red lines/escalations.
- Surface — user segment/feature flag/telemetry.
- Rollback — blast-radius, kill switch, last-known-good.
Five chunks. That’s already flirting with 🤯.
Design goal: compress this to three:
- One page (runbook) that resolves 1–5 in a single view.
- One function that wires the happy path.
- One command to ship and one to roll back.
If a new hire cannot ship a safe patch in 24 hours, you don’t have a talent problem — you have a load problem.
Leadership: Managing Load, Not Just People
Great AI leadership is a cognitive load discipline:
- Name invariants. Give every non-obvious rule a name and a home.
- Budget entropy. New feature? What’s the onboarding cost in hours.
- Prefer deep modules. Powerful, boring interfaces.
- Delay decisions responsibly. Reversible today beats irreversible theory.
- Make feedback loops first-class. Capture → attribute → act → learn.
- Observability as narrative. Traces should tell the story a human can follow in a minute.
Field Notes (from shipping AI products)
- One function to read when lost. A top-level “wire-up” that stitches the system; everything starts there.
- Artifacts over rumors. Every live prompt/model/policy has an ID and an audit trail. If it doesn’t have an ID, it doesn’t exist.
- Monolith first, seams always. You can’t extract what you can’t see.
- The boring path wins. If a junior can’t follow it, it’s not simpler — you’re merely familiar.
- Error codes are UX. They’re user interfaces for engineers. Design them.
Micro-Patterns (copy/paste into your code reviews)
Early returns beat nested ifs
if not is_valid(req): return bad(“invalid_input”)
if not has_perms(user): return bad(“insufficient_permissions”)
return ok(do_the_thing())
Self-describing events
{ “event”:“model_inferred”,
“artifact”:“claims_extractor@2025-08-23”,
“dataset”:“claims_v4”,
“eval”:“f1=0.84 ±0.02”,
“decision”:“auto_approve”,
“explain”:“threshold@0.78, 2/3 rules satisfied” }
Seams for extraction
class VectorIndex(Protocol):
def upsert(self, items: list[Embedding]): …
def query(self, q: Embedding, k: int) -> list[Match]: …
# today
index: VectorIndex = PgVectorIndex(conn)
# tomorrow
#index = MilvusIndex(url)
Onboarding as a Diagnostic
Pair with a new engineer. Time the moments of confusion.
- >40 minutes confused? You uncovered a load hotspot.
- Document the invariant, flatten the branch, deepen the module, or delete the feature.
- Ship the simplification before shipping the feature that depends on the confusion.
Examples (the “boring wins” canon)
- A CRUD-first architecture with a small set of deep modules beats a dozen services with choreography docs.
- A Python monolith on Postgres scales farther than people expect when seams are real and profiling is routine.
- The teams that felt “smartest” often shipped the least; the teams whose code read like prose owned production.
Let junior developers review architecture. Fresh eyes are the best load meter.
Operating Rules (print this)
- Name it → Place it → Own it.
- Happy path on one screen.
- Deep modules only.
- Frameworks as jackets, not skin.
- Artifacts over anecdotes.
- Observability tells a human story.
- Monolith until extraction hurts.
- Entropy has a budget. Spend it where users feel it.
Conclusion
Imagine a paragraph that forces you to flip back three sections to decode a clause, cross-reference a diagram, and remember a prior footnote. You can parse it — eventually — but the mere act of comprehension is exhausting.
Don’t do that to your team. Don’t do that to your future self.
Reduce any cognitive load beyond what is intrinsic to the work.
Everything else is vanity.
About the author: Dr. Junaid Farooq — PhD in AI. Principal Engineer & product leader. I build AI-native systems, ship fast, and keep the headroom clear.
Originally published on Medium.
Topics: Engineering leadership