Decision Making

Mental Models for Programmers: 12 That Make You a Better Engineer

8 min read By Nemo Updated

Mental models for programmers are thinking tools that help engineers design, debug and decide more clearly. Examples include Chesterton’s fence for legacy code, Gall’s law for system design, the theory of constraints for performance and the five whys for incident reviews. They complement technical skill by improving the judgment around it.
Category
Decision guide (mental models by role)
Origin
Engineering folklore, systems thinking and management science
Best used for
Software engineers, tech leads and anyone who reviews code or runs incidents
Related
Chesterton's fence, theory of constraints, five whys

Why do programmers need mental models?

Programming is mostly decisions: what to build, what to change, what to delete and what to leave alone. Mental models give you a vocabulary for those decisions, so you can make them faster and explain them in code review instead of saying “it feels wrong.”

Below are 12 mental models for programmers, each with how it applies to real engineering work and a short hypothetical example. Several come from outside software, which is the point: good models travel. For the full library, see the mental models hub.

1. Chesterton’s fence

Chesterton’s fence says: don’t remove something until you know why it was put there. The idea comes from G. K. Chesterton’s 1929 book The Thing, where a reformer finds a fence across a road and wants to tear it down.

How it applies: That weird sleep(200), the redundant null check, the config flag nobody uses. Before deleting, run git blame, read the commit and the linked ticket, and search for the incident. Often the fence is pointless. Sometimes it’s holding back a race condition from 2019.

Example: You remove a retry loop that “obviously does nothing.” A week later a flaky upstream API takes down checkout. The commit message had explained it.

Read more: Chesterton’s fence.

2. Gall’s law

Gall’s law, from John Gall’s Systemantics (1975), says a complex system that works has almost always grown out of a simple system that worked. A complex system designed from scratch rarely works, and you can’t patch it into working.

How it applies: Resist the grand rewrite and the twelve-service architecture on day one. Build the smallest version that works end to end, put it in front of users, and let complexity arrive only when a real need forces it.

Example: One team designs a full event-driven platform before shipping a feature. Another ships a single process with a database table, then splits it when load demands. The second team usually has something running first.

There’s no separate page for Gall’s law yet; it pairs naturally with first-principles thinking, which asks what the system actually needs to do.

3. Via negativa (and YAGNI)

Via negativa means improving something by taking away rather than adding. In software, its best-known form is YAGNI: “you aren’t gonna need it,” a practice from Extreme Programming.

How it applies: The cheapest code is the code you don’t write. Every abstraction “for later,” every config option for a hypothetical customer, every speculative plugin system is code you must test, document and keep working. Deleting code is often the most valuable commit of the week.

Example: You’re about to make a function generic over five data types. Only one exists. Write it for that one; generalize when the second arrives and you know what it really needs.

Read more: via negativa.

4. Five whys

The five whys is a root-cause technique associated with Taiichi Ohno and the Toyota Production System: ask “why?” repeatedly until you reach a cause you can fix at the source.

How it applies: In postmortems, the first answer is almost never the useful one. “The server ran out of memory” is a symptom. Keep asking and you often reach a process gap: no alert, no load test, no review of a risky change. Five is a guide, not a rule; stop when you reach something you can actually change.

Example: Why did the site go down? A deploy filled the disk. Why? Logs were set to debug. Why? A config change shipped without review. Why? The config repo has no review rule. The fix is the review rule, not just the disk.

Keep postmortems blameless: the five whys should end at a system, not a person. Read more: five whys.

5. Theory of constraints

The theory of constraints, from Eliyahu Goldratt’s The Goal (1984), says a system’s throughput is limited by its single tightest bottleneck. Improving anything else barely helps.

How it applies: In performance work, profile before you optimize. If a request spends most of its time waiting on one database query, making the JSON serializer twice as fast changes nothing users notice. The same applies to teams: if code review is the bottleneck, writing code faster just grows the queue.

Example: A team rewrites a hot loop in a faster language and gains a few milliseconds. The real constraint was a missing index costing far more per request.

Donald Knuth’s line about premature optimization being “the root of all evil” (from his 1974 paper on structured programming) is the same warning in different words. Read more: theory of constraints.

6. Pareto principle

The Pareto principle says a small share of causes often produces a large share of effects. The “80/20” split is a rule of thumb.

How it applies: A few functions usually account for most of the runtime, a few modules for most of the bugs, and a few features for most of the usage. Find them with data (profilers, error trackers, analytics) and spend your effort there.

Example: Your error tracker shows a handful of exception types making up most of the volume. Fixing those beats triaging the long tail one by one.

Read more: Pareto principle.

7. Occam’s razor

Occam’s razor says that among explanations that fit the evidence, prefer the one with the fewest assumptions.

How it applies: When debugging, check the boring causes first: a typo, a stale cache, the wrong environment variable, a service that isn’t running. “The compiler has a bug” is possible and rarely true.

Example: Tests pass locally and fail in CI. Before suspecting a race condition in the test framework, check that CI is running the same dependency versions.

Read more: Occam’s razor.

8. Hanlon’s razor

Hanlon’s razor says: never attribute to malice what is adequately explained by mistakes, missing context or haste.

How it applies: In code review and incident channels, assume the person who wrote the confusing code had a reason or a deadline. It keeps reviews useful and teams able to talk about failures honestly.

Example: A teammate merged something that broke the build. The likely story is a flaky test that passed once, not carelessness. Ask before you assume.

Read more: Hanlon’s razor.

9. The map is not the territory

The map is not the territory: any model of a thing is a simplification of it. The phrase comes from Alfred Korzybski.

How it applies: Your architecture diagram, your docs, your type definitions and even your tests are maps. Production is the territory. When the two disagree, production is right. Logs, traces and real data beat your memory of how the system works.

Example: The diagram says service A calls B. Tracing shows A also calls C through a library nobody remembered. The diagram was a map from last year.

Read more: the map is not the territory.

10. Reversible vs irreversible decisions

Some decisions are cheap to undo and some aren’t. Jeff Bezos popularized the labels “two-way doors” and “one-way doors.”

How it applies: Most code changes are two-way doors, especially behind feature flags. Move fast on those. Slow down on one-way doors: public API contracts, data migrations that drop columns, database choices, anything that ships to customers’ devices. Once others depend on behavior, it’s very hard to take back.

Example: Renaming an internal function: just do it. Removing a field from a public API: deprecate, announce, wait.

Read more: reversible decisions.

11. Second-order thinking

Second-order thinking asks “and then what?” past the first effect of a change.

How it applies: Adding a cache makes reads fast (first order) and introduces invalidation bugs (second order). Adding a retry makes one call more reliable and can multiply load during an outage. Ask what each change does under failure, at ten times the load, and a year from now.

Example: A team adds aggressive retries to every client. During the next slowdown, the retries triple traffic to the struggling service and turn a slowdown into an outage.

Read more: second-order thinking.

12. Feedback loops

A feedback loop is when a system’s output feeds back into it as input. Tight loops let you learn fast.

How it applies: Fast tests, fast builds, hot reload, small pull requests and frequent deploys all shorten the loop between “I changed something” and “I know if it worked.” The shorter the loop, the more experiments you run and the less each mistake costs.

Example: A test suite that takes 40 minutes gets run before merging. One that takes 40 seconds gets run after every change.

Read more: feedback loops.

Quick reference: which model for which situation?

Situation Model to reach for
Deleting old code Chesterton’s fence
Designing a new system Gall’s law
Adding a “just in case” feature Via negativa (YAGNI)
Writing a postmortem Five whys
Slow endpoint Theory of constraints, Pareto principle
Strange bug Occam’s razor, the map is not the territory
Tense code review Hanlon’s razor
Risky migration Reversible vs irreversible decisions
Adding caching or retries Second-order thinking
Slow team velocity Feedback loops

Which biases trip up engineers?

Engineers are not immune to bias just because they work with logic. The planning fallacy explains most blown estimates: you plan from the details of the task, not from how long similar tasks actually took. Overconfidence bias shows up as “this refactor is safe, no need for tests.” And the sunk cost fallacy keeps teams pouring months into a framework migration that should have been stopped.

For technical choices with real trade-offs, such as picking a database or a framework, a weighted decision matrix beats a hallway debate. The decision tools include an interactive version, and the Mentarium Decision Coach can walk you through a big architecture call inside Claude or ChatGPT.

The Mentarium take

Most of these models say the same thing from different angles: respect what exists, start simple, and measure before you act. Chesterton’s fence respects old code, Gall’s law respects simple systems, the theory of constraints respects measurement. The engineers who seem to have great instincts usually just apply these reflexively.

Our pick for the single most underrated one is via negativa. Software teams are rewarded for adding: features, services, abstractions. Almost nobody gets promoted for the module they deleted, yet removed code can’t break, doesn’t need patching and doesn’t confuse the next hire.

For big technical decisions, use the Decision Weight Score. A library choice you can swap in a week scores low: pick and move on. A database migration or a public API design scores high: run the full Mentarium Decision Loop (Frame → Map → Stress-test → Decide → Review), including a pre-mortem, and write the decision record down. Future you, reading git blame, will be grateful for the fence’s explanation.

FAQ

What mental models should every programmer know?

Chesterton's fence, Gall's law, the theory of constraints and the five whys cover the most daily ground: changing old code, designing new systems, fixing performance and learning from incidents. Add Occam's razor for debugging and reversible vs irreversible decisions for deploys.

What is Gall's law?

Gall's law, from John Gall's book Systemantics (1975), says that a complex system that works has almost always evolved from a simple system that worked. A complex system designed from scratch tends not to work and can't easily be patched into working.

Is YAGNI a mental model?

YAGNI ("you aren't gonna need it") is a practice from Extreme Programming, but it's an application of a broader model, via negativa: improving something by removing or not adding, rather than by adding. Every line you don't write is a line you never have to maintain.

How do mental models help with debugging?

They make you test the cheapest likely explanation first (Occam's razor), remember that your mental picture of the system is not the system (the map is not the territory), and keep asking why until you reach a cause you can fix (five whys).

Why do software estimates go wrong so often?

The planning fallacy: people underestimate how long their own tasks will take, even when they know similar tasks ran over. Using base rates from past work, such as how long the last five similar tickets really took, usually beats estimating from the details of the new one.

Contact

Orders, downloads, corrections and ideas. We reply by email.