Why the Fastest Systems Sometimes Refuse to Change Their Minds

Kai Nguyen

Hatched by Kai Nguyen

Jul 31, 2026

9 min read

68%

0

The strange bargain behind speed

Why do two ideas that seem almost unrelated, file descriptors and immutable strings, point to the same design truth? Because both are answers to the same problem: how do you move information through a system without letting the system itself become unreliable? One answer is to make the path explicit. Another is to make the data itself unchangeable. Both strategies reduce surprise, and surprise is the enemy of coordination.

At first glance, network sockets and immutable objects live in different universes. One concerns communication across machines, the other concerns what a value is allowed to do after it exists. But they are both about containing chaos. A socket gives programs a disciplined way to talk. An immutable string gives a program a disciplined way to remember. In both cases, the system becomes faster not by doing more, but by refusing to improvise.

The deepest performance trick is often not acceleration. It is removing ambiguity.

That is the real connection here. TCP, UDP, file descriptors, and immutable objects are all different expressions of the same architectural question: where should change be allowed, and where should it be forbidden?

A socket is not just a pipe, it is a contract

It is tempting to think of a socket as a glorified pipe, a channel that shuttles bytes from one place to another. But the more interesting idea is that a socket is a contractual boundary. It says: here is the interface through which one program may influence another, and here are the rules governing that influence.

That matters because distributed systems are hostile to assumptions. Data can be delayed, duplicated, reordered, or lost. TCP exists to reduce those uncertainties by guaranteeing sequential, error free delivery. UDP, by contrast, strips away much of that accounting. It is faster because it says, in effect, “I will move the packet, but I will not babysit it.”

This is not merely a technical choice. It is a philosophical one. TCP optimizes for consistency of arrival, while UDP optimizes for speed of motion. One treats communication as a promise that must be kept. The other treats it as a best effort that must remain lightweight enough to be useful in motion sensitive contexts.

The real lesson is that every system has a budget for certainty. The more certainty you demand, the more bookkeeping you must perform. The less bookkeeping you perform, the more responsibility you push outward to the caller, the network, or the application.

Immutable data is the TCP of memory

Now consider strings in Python. A string is immutable, which means once it is created, it cannot be changed. At first this may seem like a limitation. Why not allow direct modification? Why copy when you could edit in place?

Because immutability is a way of making references reliable. If a string can never change, then anyone holding that string can trust it forever. There is no hidden mutation, no surprise rewrite, no accidental side effect from some other part of the program. The value becomes a stable fact rather than a movable target.

That stability is incredibly valuable in software. When many parts of a program share the same object, mutability creates a coordination problem. If one part changes the object, every other part that depends on it must somehow know. That means tracing ownership, timing, and side effects. Immutability removes that burden. It says: the object will not betray your expectations.

In that sense, an immutable string behaves like TCP. Not because it moves across a network, but because it provides ordered, dependable semantics. You do not need to ask whether this exact string still means what it meant a moment ago. It does.

Mutability is power, but immutability is trust.

And trust is a performance feature. When objects do not change under your feet, reasoning becomes cheaper. Testing becomes simpler. Concurrency becomes less dangerous. The program spends less energy defending against its own state.

The same architecture appears at every layer

The most interesting overlap between sockets and immutability is that both reveal a larger principle: systems become manageable by pushing complexity to the edges and simplifying the core.

Networking does this through encapsulation. The OSI model shows layers upon layers, from physical signals all the way up to the application. Each layer hides the mess beneath it and exposes a smaller, cleaner problem to the layer above. Your application does not need to know how bits become voltages, or how packets traverse links, in order to send a message. It only needs to know the interface offered by the socket.

Immutability does something similar within memory. Instead of exposing an object as a bundle of mutable parts that any code may alter, it freezes the value and presents it as a stable unit. The rest of the program can rely on that unit without constantly checking whether it has been changed.

Here is the deeper pattern:

  1. Encapsulation reduces the surface area of failure.
  2. Immutability reduces the surface area of surprise.
  3. Protocols reduce the surface area of negotiation.

These are all strategies for making complexity tractable. They do not eliminate complexity. They relocate it. The low level becomes more complicated so that the high level can become more legible.

This is why the Unix idea that everything is a file feels so powerful. It collapses many kinds of input and output into a unified abstraction. That unification is not about elegance alone. It is about reducing the number of ways the system can miscommunicate with itself.

Why speed often comes from refusing to remember

There is a common intuition that safer systems must be slower. Sometimes that is true, but the more subtle truth is that selective forgetting can make systems faster.

UDP is faster than TCP because it refuses to remember enough information to guarantee delivery order or retries. It keeps the transaction small. Immutable objects are fast to reason about because they refuse to remember alternative histories. Once created, they are fixed. They do not need locks, version checks, or defensive copying when shared carefully.

This creates an important design question: what should your system remember, and what should it treat as read only truth?

Consider a chat application. The network layer might use TCP to ensure that a message arrives intact. But once the message arrives, the content itself should often be immutable. If two parts of the app display the same message, neither should be able to alter it unexpectedly. The transport may need to preserve sequence, while the payload needs to preserve identity.

Or consider a game. Position updates might use UDP because a stale frame is worse than a missed frame. But the player name, item definitions, and match rules are much better treated as immutable. If those change midstream, the game world becomes incoherent.

This suggests a useful mental model:

Mutable where decisions are being negotiated. Immutable where decisions are already settled.

That line may be one of the most practical design principles in software. It tells you not to choose between mutability and immutability absolutely, but to assign each to the phase where it belongs.

A mental model: the shipping dock and the sealed crate

Imagine a shipping dock. The dock is noisy, dynamic, and full of motion. Forklifts move pallets, trucks arrive, labels get scanned, and packages are routed onward. That is the world of sockets and protocols. It is a world of transfer, not permanence.

Now imagine the sealed crate inside one of those packages. Once sealed, its contents should not be changed by anyone along the route. That is the world of immutability. The crate is not responsible for transportation. Its job is to preserve integrity.

This distinction is profound because it prevents a common design error: trying to make the thing being transported do the work of transport. A string should not need to know how it will be transmitted over the network. A socket should not need to know the semantic meaning of every payload it carries. Each layer should protect its own domain.

When systems fail, they often fail because boundaries are blurred. A mutable object leaks internal state. A custom protocol embeds too much application logic. A component starts to act like both courier and cargo. Then every change becomes a ripple across the whole stack.

By contrast, clean boundaries create freedom. The network can optimize routing without changing the payload. The payload can remain stable without knowing the route. That separation is what makes large systems possible at all.

The hidden cost of flexibility

Flexibility sounds like an unqualified good. Mutable objects are flexible. Fire and forget datagrams are flexible. But flexibility always has a bill attached.

With mutability, the bill is paid in coordination. Someone, somewhere, must ensure that updates happen in the right order, by the right owner, at the right time. With UDP, the bill is paid in responsibility. The application must tolerate loss, duplication, or reordering. If it does not, the flexibility turns into bugs.

This is why mature systems are not simply “as flexible as possible.” They are selective. They make some things rigid on purpose so that other things can move quickly. They spend certainty where it buys the most leverage.

That tradeoff applies just as much to code design as it does to network design. If a value changes often and locally, mutability may be appropriate. If it is shared widely, immutability usually wins. If messages can be dropped without destroying the experience, UDP may be enough. If every packet matters, TCP is worth the overhead.

The challenge is not to avoid overhead entirely. The challenge is to place overhead where it is cheapest.

Key Takeaways

  • Use immutability for shared truth. If many parts of a program rely on the same value, make it stable so nobody can change it behind the others’ backs.
  • Use stronger transport guarantees when order matters more than raw speed. If correctness depends on every byte arriving intact and in sequence, pay for TCP like reliability insurance.
  • Use weaker guarantees when freshness matters more than completeness. If a stale update is worse than a missing one, the lighter contract of UDP can be the right tool.
  • Separate payload from path. Keep the thing being communicated independent from the mechanism that moves it.
  • Ask where surprise is most expensive. Make that part rigid. Leave flexibility only where the system can afford it.

Conclusion: robustness is the art of choosing what must never change

The deepest lesson in both networking and immutability is not about bytes or objects. It is about discipline. Robust systems are built by deciding, with care, which parts of reality are allowed to shift and which parts must remain fixed.

A socket is a disciplined way for programs to speak. An immutable string is a disciplined way for values to persist. One governs motion, the other governs identity. Together they reveal a surprising truth: the fastest systems are often not the most fluid ones. They are the ones that know exactly where fluidity belongs, and where it would only create noise.

In other words, speed is not just about moving faster. It is about making fewer promises in the wrong place.

When you design software, do not ask only how to let things change. Ask where change should be forbidden so that the rest of the system can finally trust itself.

Sources

← Back to Library

Hatch New Ideas with Glasp AI 🐣

Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)

Start Hatching 🐣