Paper 02: The Law of Chaos — Decoding Entropy in Distributed Architecture
Table of Contents

I. Prelude: What Happens When One Block Becomes Many #
Splitting a monolith into microservices is not a code-partitioning exercise. The number of states the system can occupy grows by multiplication, not addition, and three measurable consequences follow: tail latency becomes the default rather than the exception, the system can get slower as you add resources, and a retry storm can hold it down long after the original trigger is gone. This paper works through each number, then reaches the way out: instead of demanding immediate consistency everywhere, use operations that only add and never retract — the basis of the CALM theorem and CRDTs — so replicas converge on their own with no coordinator involved.
The previous paper traced the shift from logic to probability inside an AI model. That boundary does not stop at the model. It runs through every node of the distributed systems we already operate daily.
The part that gets missed: splitting a monolith into microservices is not a code-partitioning exercise. It is a change of state for the whole system. Before, you had a system where things were either right or wrong. After, you have one that is only right with some probability.
The word “entropy” in the title is used in its original sense, and there is nothing mystical about it: entropy is the count of ways a system could currently be arranged. The more possible configurations, the less you can predict which one you are standing in. This entire paper is the story of how that number swells, and how to live with it.
II. The Cartesian Trap: When Control Becomes an Illusion #
Why do traditional verification methods collapse at distributed scale? One word: explosion. The state space1 does not grow — it detonates.
Inside a single block, shared variables live in one memory space and are bound together at compile time. Split into $n$ services and the total number of reachable states $\Omega$ is not a sum but a product:
$$ \Omega = |C| \times \prod_{i=1}^{n} |S_i| $$In words: each service is free to sit in any of its own states independently of the others, so the number of combinations is the product of all of them. $|S_i|$ is the internal state count of service $i$; $|C|$ is the state of the network channel joining them — packets can be lost, delayed, duplicated, or delivered out of order.
Put numbers in. Ten services, 100 states each: $100^{10} = 10^{20}$ combinations. That figure exceeds the number of seconds since the Big Bang by roughly ten billion times — and it still excludes the channel. No verification tool walks that space, which makes any claim of “we tested all the cases” false by definition.

III. Why the Whole Is Not the Sum of Its Parts #
Systems theory draws a hard line between two words that get used interchangeably: complicated, in the mechanical sense, and complex, in the ecological sense.
A mechanical watch holds hundreds of gears — very complicated, but understand each gear and you understand the watch. A microservice network does not work that way: it has emergence2, behavior that appears only at the whole-system level and lives in none of the components. The retry storm in Section V is the cleanest example: no service contains a line of code that causes it; it arises from all of them retrying at once.
In notation, that fact is a single “not equal” sign:
$$ f(x_1 + x_2 + \dots + x_n) \neq f(x_1) + f(x_2) + \dots + f(x_n) $$In words: measure each service in isolation, add up the results, and you do not get the performance of the system with all of them running together. The difference is coordination cost, and it always subtracts.
Neil Gunther’s Universal Scalability Law pins this to a curve. Its content, without the equation: adding resources buys you two opposing things. The gain is parallelism; the cost is that every node must talk to every other node, and that cost grows with the square of the node count. Past a certain point the second term wins and adding machines makes the system slower3. Anyone who has added workers to a queue and watched throughput drop has met that part of the curve.
IV. At Scale, Tail Latency Is the Default #
Architects get misled by averages. What determines user experience is not p50 but p99 — the slowest 1% of requests. That sounds like a rounding error until you do the multiplication.
Jeff Dean and Luiz André Barroso made the point in “The Tail at Scale.” Suppose one request must call $n = 100$ services in parallel, and each service has just a 1% chance of responding slowly. The probability the whole request is dragged down:
$$ P(\text{slow request}) = 1 - P(\text{fast})^n = 1 - (0.99)^{100} \approx 63.4\% $$In words: the request is fast only when all one hundred services are fast, and each one has a small door open to slowness. Multiply a hundred small doors together and “rarely slow” turns into “slow almost two times out of three.” At $n = 10$ the figure is 9.6%; at $n = 100$ it is 63.4%. Nothing is broken in that system — it is arithmetic.
The practical countermeasure is a hedged request: send the request, wait until the p95 mark, and if no answer has arrived, fire a duplicate at another replica and take whichever returns first. You cannot make a slow request fast, but you stop depending on exactly one path. The cost is roughly 5% extra traffic — picking the p95 mark is precisely what keeps that overhead small.
1% slow"] Fan --> S2["Service 2
1% slow"] Fan --> Sn["Service ...100
1% slow"] S1 --> Join{Wait for every response} S2 --> Join Sn --> Join Join -->|"63.4% of the time at least one is slow"| Slow["Slow request"] Join -->|"36.6%"| Fast["Fast request"] Slow -.->|"past the p95 mark, fire a duplicate at a replica"| Hedge["Hedged request"] style Slow fill:#f96,stroke:#333,stroke-width:2px style Hedge fill:#f96,stroke:#333,stroke-width:2px
V. When the System Stays Down After the Cause Is Gone #
The most unpleasant failure class in distributed systems is the one that continues after the original trigger disappears. That is a metastable failure4: the root cause ended long ago, the system is still lying in the collapsed state, and it will not get up on its own.
The usual culprit is the retry storm. Latency crosses the timeout, clients retry automatically. Each retry is a new request added to an already-saturated system, pushing utilization $\rho$ toward 1. Kingman’s formula states the price:
$$E[W] \propto \frac{\rho}{1 - \rho}$$Here $\rho$ is utilization — arriving load divided by processing capacity — and $E[W]$ is mean wait time. In words: wait time does not scale with load, it scales with the capacity that remains free, and that remainder is shrinking. Substitute: $\rho = 0.5$ gives a factor of 1; $\rho = 0.9$ gives 9; $\rho = 0.99$ gives 99. The stretch from 90% to 99% load multiplies wait time elevenfold, even though the dashboard makes it look like “9% more.”
Little’s Law closes the loop: $L = \lambda W$ — the amount of work sitting in the system equals arrival rate times how long each item stays. When $W$ explodes, $L$ explodes with it. The swelling queue drains the thread pool and memory, new requests time out, clients retry again. The loop feeds itself, which is why removing the original trigger rescues nothing.
Two layers of countermeasure:
- Exponential backoff with jitter. Backoff widens the gap between attempts. Jitter adds a random offset to that gap — without it, thousands of clients that failed at the same moment retry at the same moment, producing synchronized waves (the thundering herd).
- Circuit breaker. After a run of consecutive failures, cut the call path entirely for tens of seconds and fail fast. That gives the downstream service the empty space it needs to drain its backlog — something retries will never grant it.
VI. The Way Out: Orchestrating Chaos Instead of Fighting It #
If immediate consistency and synchronous coordination guarantee collapse under load, what survives? The answer sits at the data layer.
The CALM theorem (Consistency As Logical Monotonicity) states something surprisingly simple: an operation that only adds information and never retracts it can run safely on every replica with no central coordinator at all.
The distinction is easiest by example:
- Monotonic (runs without coordination): adding a person to an attendee list; incrementing a view counter; recording “this order is paid.” Two nodes processing in different orders still land on the same result.
- Non-monotonic (coordination required): decrementing stock when one unit remains. Here the order decides who gets the item, so no amount of cleverness avoids an agreement round.
From this comes the CRDT (Conflict-Free Replicated Data Type): a data structure that merges itself, no locks required. Three properties make it work, stated plainly: the order changes arrive in does not matter, grouping them any way gives the same result, and receiving the same change twice is harmless. Put together, every replica converges on the same state, including after a network partition. This is the machinery behind editors where several people type at once.
For non-monotonic business logic — decrementing stock, debiting money — there is no trick. There you accept eventual consistency and implement it with Sagas and the Outbox pattern: every step gets a compensating step, and every change is written together with its event in one transaction so nothing is ever lost.
Engineers do not fight entropy. Engineers choose where to pay for it.
VII. References & Further Reading #
- Dean, J., & Barroso, L. A. (2013). The Tail at Scale.
- Ameller, M., et al. (2024). Micro Services: Methodologies, Challenges, and Trends.
- Hellerstein, J. M., & Alvaro, P. (2020). Keeping CALM: When is Distributed Consistency Easy?.
- DoorDash Engineering (2022). Failure Mitigation for Microservices: An Intro to Aperture.
- Bhatti, S. (2023). Testing Distributed Systems Failures with Interactive Simulators.
- Golshani, H. (2021). Understanding CAP Theorem in Microservices.
- Montesi, F., et al. Modeling Cascading Failure Propagation through Dynamic Bayesian Networks.
State space explosion: the number of reachable system states grows exponentially with the number of components, past the point where any verification or simulation tool can enumerate them. ↩︎
Emergence: behavior that appears only at the whole-system level and cannot be reduced to, or predicted from, the behavior of any single component. ↩︎
Retrograde scalability: the region of Neil Gunther’s curve where adding resources (nodes, threads, processes) reduces total throughput, because coordination cost between components outgrows the benefit of running in parallel. ↩︎
Metastable failure: a system stuck permanently in a collapsed state because a feedback loop — usually retries — sustains itself, so it cannot recover even though the root cause is gone. Escaping usually requires cutting load from outside. ↩︎