Surface Tension in Software: How Constraints Keep Systems Whole
Share this article
Surface Tension in Software
When a developer presses a finger against water, the liquid pushes back. That invisible resistance—surface tension—keeps the liquid whole even when disturbed. In software, a comparable force keeps systems intact amid change. It is not a feature you enable; it is an emergent property of carefully crafted constraints.
The Physics of Integrity
Good software behaves like a droplet: it resists deformation, yet it can flex without breaking. The difference between a calm codebase and one that leaks entropy lies in integrity—the way a system manages side effects without losing its shape. Integrity is not a declaration; it is the sum of small, consistent forces: clear boundaries, honest interfaces, and a consistent language.
Purity, Immutability, Idempotence
These principles are the physics that keep a system in orbit:
- Pure functions return the same output for the same input, with no hidden effects.
- Immutable data cannot be changed after creation, only transformed.
- Idempotent operations produce the same result no matter how many times you apply them.
When these laws are enforced by the type system, impossible states vanish, and refactors stop rippling outward.
A Case Study: User Profile State
Consider a UI that fetches user data. A naïve representation allows contradictory states:
interface UserProfile {
loading: boolean;
error?: string;
data?: User;
}
Now, loading is false, error is present, and data is also present—a nonsensical combination that forces defensive checks everywhere.
A more disciplined approach uses an algebraic data type that enumerates the valid states:
enum UserProfile {
Loading,
Failed(string),
Loaded(User),
}
With this design, the impossible states are eliminated at compile time. Pattern matching forces the developer to handle every valid case, and only those. The type system becomes the membrane that holds the shape.
Good patterns and abstractions behave like membranes. They don’t restrain motion; they guide it.
This quote captures the essence of surface tension in code: abstractions that contain side effects without stifling movement.
Implications for Developers
- Reduced Cognitive Load – When the type system guarantees invariants, developers no longer need to guard against impossible states.
- Safe Refactoring – Changes ripple only within well‑defined boundaries, preventing cascading failures.
- Predictable Evolution – Systems that enforce constraints evolve gracefully, as new features must fit within the existing invariant framework.
However, too much constraint can freeze a system, turning it into an ice block that cannot adapt. The art lies in balancing order and freedom, allowing motion without collapse.
The Balance of Rigidity and Flow
The best systems live in a delicate equilibrium: they are rigid enough to prevent entropy but fluid enough to accommodate change. This balance mirrors the relationship between a coder’s discipline and creativity—each shaping the medium until form and motion become one.
In practice, achieving this equilibrium means:
- Explicitly modeling states rather than relying on ad‑hoc flags.
- Favoring pure functions in business logic and UI rendering.
- Using immutable data structures to avoid accidental mutation.
- Designing APIs that expose only the necessary surface.
When these practices converge, surface tension naturally emerges, and the software behaves like a well‑formed droplet—stable yet adaptable.
Source
This article is based on Surface Tension of Software by Stelios, available at https://iamstelios.com/blog/surface-tension-of-software/