Surface Tension

If you press your finger against water, it pushes back. That invisible resistance, surface tension, keeps the liquid whole even when disturbed.

Good software has something like it. Some systems hold together when you change them; others leak at the slightest touch. The difference lies in integrity — the way a system manages its side effects without losing its shape.

I’ve seen codebases that felt strangely calm, where every possible state meant something real and nothing arbitrary could slip in. Others allowed nonsense to exist, and from there, entropy spread quietly like cracks beneath paint.

Type systems, invariants, and boundaries exist to make meaning explicit. They define where things start and stop — what’s allowed, and what isn’t. Without that structure, logic turns soft; assumptions spread, and the system eventually folds under its own ambiguity.

Systems stay whole when their structure insists on coherence: clear boundaries, honest interfaces, consistent language. Each adds its own gravity, and together they make a world that holds. Stability isn’t declared; it emerges from the sum of small, consistent forces.

Constraint-driven design makes that gravity visible. In software, these laws have names: purity, immutability, idempotence, transparency, composability. They’re the physics that keep a system in orbit.

Pure functions return the same output for the same input, with no hidden effects. Immutable data can’t be changed after creation, only transformed. Idempotent operations produce the same result no matter how many times you apply them. These aren’t academic exercises — they’re physics that prevent the impossible.

But let one careless change skip a step and the world tears.

Consider a UI that fetches user data. Without tension, it leaks:

struct UserProfile {
    loading: bool,
    error: Option<String>,
    data: Option<User>,
}

What does it mean when loading is false, error is Some, and data is also Some? The type allows nonsense. You write defensive checks everywhere. Every render must guess which combination is real.

Now give it shape:

enum UserProfile {
    Loading,
    Failed(String),
    Loaded(User),
}

The impossible states vanish. You can’t be loading and failed. Pattern matching forces you to handle every valid case, and only those. The type system becomes the membrane — it holds the shape.

In well-shaped systems, nonsense simply cannot exist because the universe of the program doesn’t contain it. You don’t defend against the impossible. You design a world where the impossible has no syntax.

When those laws are clear, surface tension appears on its own — you feel it when refactors stop rippling outward, when a change bends without breaking, when boundaries push back just enough to preserve meaning.

Good patterns and abstractions behave like membranes. They don’t restrain motion; they guide it. They contain side effects the way water’s surface holds its ripples — movement without spillage, energy without collapse. Parsing, typing, composition: these are the laws of motion that let a system stay whole when disturbed.

There’s an old engineering instinct that says, “We’ll handle it later if it happens.” But in coherent systems, it simply can’t happen. You can only move through valid states, and that constraint is what makes motion possible at all.

But tension has its limits. Too much and water hardens into ice — flawless, unmoving, lifeless. Software can freeze the same way, becoming so rigid it forgets to flow. Balance lives somewhere between order and change, between holding on and letting go.

The best systems live there, in that delicate balance where structure meets freedom. And perhaps that’s where the worlds of hackers and painters quietly meet, each shaping their medium until form and motion become one.

That precise balance is the true art of code.


Further reading:
Rich Hickey’s Simple Made Easy — a classic talk on why simplicity comes from separation, not convenience.