· Updated

Why Programmers Should Study Physics

Physics teaches habits programmers need badly: invariants, useful approximations, dimensional thinking, phase transitions, and respect for entropy.

I did not study physics because I had a noble plan.

I studied it because I was curious, slightly stubborn, and apparently the kind of person who looks at free time and thinks, “What if I used this to feel mathematically inadequate?”

But the strange thing is that physics kept improving how I wrote software.

Not because I started calculating projectile motion in TypeScript. Nobody needs a parabolic arc in their login flow. The useful part was the way physics trains you to think: find invariants, name assumptions, estimate before measuring, respect units, and stay suspicious of systems that behave smoothly until they very much do not.

Programming has more in common with physics than it first admits.

Both involve building models of messy systems and then being surprised when reality files a bug report.

Find What Stays The Same

Physics loves conserved quantities.

Energy. Momentum. Charge. Things that remain constant even while everything else is moving around and causing drama.

Software has the same idea. We call them invariants because “conserved quantities” would make sprint planning sound too cool.

An invariant is something that must always be true:

  • a sorted list stays sorted
  • a cache key maps to the data shape it promises
  • a graph that claims to be a DAG has no cycles
  • a chess position never has two white kings
  • a route manifest maps one URL pattern to one handler

When a system is confusing, invariants give you something solid to hold.

Before coding the behavior:

  What must always be true?
        |
        v
  Where can it be violated?
        |
        v
  How do we assert it at the boundary?

That last question matters. An invariant you never check is basically a motivational quote.

I like writing invariants down before writing the moving parts. It changes the design. Instead of asking “how do I implement this?”, you ask “what state should be impossible?”

That is a much sharper question.

Units Are Types With Receipts

Physicists are trained to carry units through equations.

You cannot add meters to seconds. You cannot compare kilograms to velocity. If the units do not work, the answer is wrong before you even plug in numbers.

Programmers, meanwhile, will pass number into anything and then act betrayed when production discovers the difference between milliseconds and seconds.

The classic horror story is the Mars Climate Orbiter. One system used imperial units, another expected metric, and $327 million went off to become space confetti.

Most of us will not crash a spacecraft. We will merely do smaller, more affordable nonsense:

setTimeout(refreshSession, token.expiresIn);

Is expiresIn seconds? Milliseconds? Minutes? Vibes?

A better model is to make units visible:

type Milliseconds = number & { readonly unit: "ms" };
type Seconds = number & { readonly unit: "s" };

function seconds(value: number): Seconds {
  return value as Seconds;
}

function toMilliseconds(value: Seconds): Milliseconds {
  return (value * 1000) as Milliseconds;
}

This is not about making TypeScript fancy. It is about refusing to let incompatible ideas wear the same costume.

Physics teaches you to ask: what kind of quantity is this?

Types should do the same.

Models Are Supposed To Be Wrong

One of the most useful physics quotes is:

All models are wrong, but some are useful.

At first this sounds like a resignation. It is not. It is permission to simplify on purpose.

A model is a controlled lie. The skill is knowing what it lies about.

In software, we build models constantly:

  • this cache assumes reads are stable
  • this rate limiter assumes traffic is roughly distributed
  • this queue assumes workers eventually catch up
  • this dependency graph assumes imports are statically discoverable
  • this UI state machine assumes users cannot be in two modes at once

All wrong, in some way.

The mature move is not to pretend the model is complete. The mature move is to write down the assumptions and add signals for when they stop holding.

Useful model:

  assumption -> implementation -> measurement -> correction

Dangerous model:

  assumption -> implementation -> denial -> incident review

Physics makes peace with approximation. Programmers should too. The goal is not a perfect model. The goal is a model that is accurate enough, cheap enough, and honest about where it breaks.

Estimate Before You Measure

Physicists estimate constantly.

Not because estimates are perfect, but because being wrong by 10x teaches you something. Being wrong by 1000x teaches you something urgently.

Before doing performance work, I try to write the napkin math first:

packages: 45
avg imports per package: 6
edges: ~270
traversal: O(V + E)
expected runtime: boringly small

If the actual runtime is 3 seconds, something is weird. Maybe I am parsing files repeatedly. Maybe I am hitting the filesystem in a loop. Maybe my “simple graph traversal” has secretly invited the entire monorepo to dinner.

The estimate gives surprise a shape.

Without a prediction, measurement is just a number. With a prediction, measurement becomes feedback.

This habit is small but powerful:

  1. Guess the order of magnitude.
  2. Measure.
  3. Ask why the guess was wrong.
  4. Update your intuition.

That loop is how you get calibrated. Also humbled. Often both before lunch.

Broken Symmetry Is A Smell

In physics, symmetry means a transformation that leaves something unchanged.

In programming, I use a less formal version: if two cases look almost the same but the code treats them wildly differently, I get suspicious.

Sometimes the asymmetry is real. Often it is accidental.

Example:

if (items.length === 0) {
  return renderEmptyState();
}

return renderList(items);

This might be correct. Empty states are real product states.

But this pattern:

if (user.type === "admin") {
  return getPermissionsFromA(user);
}

return getPermissionsFromB(user);

should make you pause. Are admins truly a separate model, or did history happen to the codebase and nobody cleaned up after it?

Broken symmetry asks:

Are these cases different because the domain says so,
or because the code drifted?

That question has saved me from many special-case tunnels. You walk into one if, and suddenly you are maintaining a museum of exceptions.

Watch For Phase Transitions

Water at 99 degrees Celsius is hot water.

Water at 100 degrees Celsius is not “slightly hotter water” in the same practical sense. It becomes steam. The system changes regime.

Software does this too.

A queue with 100 jobs is fine. A queue with 10,000 jobs may be a different system. A cache hit rate of 90 percent is healthy. A cache hit rate of 20 percent might mean the database is now the cache, which is a sentence that should make everyone sit up straighter.

Smooth change:

  more traffic -> more CPU

Phase transition:

  more traffic -> cache misses -> DB saturation -> retries -> more traffic

The dangerous part is that the graph looks boring until it does not.

So I like asking:

  • where does slow become down?
  • where does retry become amplification?
  • where does backlog become permanent?
  • where does memory pressure become a restart loop?
  • where does a harmless migration become a coordinated rollout?

Physics teaches you to look for regime changes. Production teaches you the same thing, but with more Slack notifications.

Entropy Always Wins Unless You Work

Entropy is not just a fancy way to say “mess.”

In software, entropy shows up when the environment around the code changes:

  • dependencies age
  • product requirements shift
  • teammates leave
  • new teammates join
  • patterns that once made sense become awkward
  • feature flags outlive the features
  • documentation gets stale

Even untouched code decays in usefulness because the world around it moves.

That is why maintenance is not optional. Refactoring is not vanity. Documentation is not a side quest. Tests are not ceremony.

They are the work required to keep the system ordered.

Without maintenance:

  clear model -> exceptions -> folklore -> fear

With maintenance:

  clear model -> drift noticed -> model updated -> trust preserved

Entropy does not make you a bad engineer. It makes you an engineer inside time.

Very rude of time, honestly, but here we are.

The Point

You do not need to solve Maxwell’s equations to become a better programmer.

But physics gives you mental moves that transfer beautifully:

  • name what must stay true
  • carry units through the system
  • make useful approximations
  • predict before measuring
  • notice suspicious asymmetry
  • expect phase transitions
  • respect entropy

Those habits make you calmer around complex systems. They make you less likely to worship your first model. They make you more willing to be wrong in a way that teaches you something.

And that might be the most useful part.

Physics does not teach you that the world is simple.

It teaches you how to think clearly when it is not.