Failure Is the Default
at scale, something is always brokenWith enough machines, disks, and network links, something is failing right now. You cannot design failure out โ you design to contain it (stop it spreading), degrade gracefully (serve less rather than nothing), and recover automatically. Every pattern on this page is one of those three verbs.
The patterns compose. Timeouts bound how long you wait, retries recover from blips, circuit breakers stop hammering a downed dependency, bulkheads contain the blast radius, and load shedding sheds the least-important work first. Together they break the cascade in the last section.
Timeouts
the one every remote call needsEvery remote call can hang forever, and a caller waiting forever is a caller holding a thread, a connection, and memory forever. A missing timeout is how "slow" becomes "down" โ the slow dependency doesn't just fail its own requests, it exhausts the caller's resources and takes the caller down too.
Retries
recover from blips without making a stormRetries recover from transient failures โ a dropped packet, a momentary blip. Two hard rules: retry only transient errors (a 500, a timeout โ never a 400), and only idempotent operations, or a retry double-charges the customer (Concurrency Control).
Naive retry (all clients retry at the same instant):
fail โโถ retry โโถ retry โโถ retry synchronized WAVES hammer the
โ โ โ โ recovering service back down
โโโโโโโโโดโโโโโโโโโดโโโโโโโโโ
Exponential backoff + JITTER:
wait 1s ยฑ rand โโถ wait 2s ยฑ rand โโถ wait 4s ยฑ rand
spacing grows, randomness spreads clients out โ no thundering wave
Circuit Breaker
stop hammering a dependency that's downWhen a dependency is clearly down, retrying is worse than failing fast โ you waste your own resources and pile load on something that can't answer. A circuit breaker watches the failure rate and, once it crosses a threshold, opens: calls fail immediately without even trying, giving the dependency room to recover.
failures exceed threshold
โโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโถ โโโโโโโโโโ
โ CLOSED โ โ OPEN โ fail fast,
โ normal โ โโโโโโโโโโโโโโโโโโโโโโ โ no callsโ don't even try
โโโโโโโโโโ probe succeeded โโโโโโฌโโโโ
โฒ โ after a cooldown
โ โโโโโโโโโโโโโ โ
โโโโโโโ โ HALF-OPEN โ โโโโโโโโโโโโโโโโ
probe โ let a few โ
fails โ โ test calls โ probe passes โ close again
reopen โ through โ
โโโโโโโโโโโโโ
After a cooldown it goes half-open, letting a few probe requests through. If they succeed, it closes and normal traffic resumes; if they fail, it opens again. The breaker protects both sides: the caller stops wasting time on doomed calls, and the struggling callee gets breathing room instead of a stampede.
Bulkheads
isolate resources so one leak doesn't sink the shipNamed after a ship's watertight compartments: a breach floods one, not the whole hull. In software, you give each dependency its own isolated pool of resources โ connections, threads, instances โ so one slow dependency can only exhaust its pool, not drain every thread in the service.
WITHOUT bulkheads: one shared thread pool
service A slow โโถ all threads block on A โโถ calls to healthy B, C also starve
WITH bulkheads: a pool per dependency
โโโ pool A โโโ A is slow โ only pool A fills
โโโ pool B โโโ B still served
โโโ pool C โโโ C still served the failure is contained to A
Load Shedding & Graceful Degradation
serve less rather than fail entirelyUnder overload, the choice is not "serve everyone" โ it's "serve some, or serve no one." Load shedding drops low-priority work so high-priority work survives: reject non-critical requests, turn off expensive features, return early. Graceful degradation serves a worse-but-real answer โ stale cache, partial results, a default recommendation โ instead of an error.
Cascading Failure Anatomy
how one slow DB takes down everythingCascading failure is how a small problem becomes a total outage. Walk one chain, then see how each pattern above breaks a link.
1. DB slows down (a bad query, a lock, a spike)
โ โ TIMEOUT bounds the wait
2. app threads block waiting on the DB
โ โ BULKHEAD isolates the DB pool
3. thread pool exhausts; healthy requests can't get a thread
โ โ LOAD SHEDDING drops low-priority work
4. health checks time out โ node marked unhealthy, pulled from the LB
โ โ shallow health check (Discovery)
5. its traffic reshuffles onto the remaining nodes
โ โ CIRCUIT BREAKER stops the DB pile-on
6. now THEY are overloaded โ they tip over โ total outage
โ BACKOFF + JITTER prevents retry waves
Quick Reference
patterns on one card| Pattern | Protects against | Key parameter | Gotcha |
|---|---|---|---|
| Timeout | Hung calls exhausting resources | Deadline (propagated) | Missing one turns slow into down |
| Retry | Transient blips | Backoff, jitter, budget | Amplifies load if stacked |
| Circuit breaker | Hammering a downed dependency | Failure threshold, cooldown | Tune so it doesn't flap |
| Bulkhead | One dependency draining all resources | Pool size per dependency | Over-partitioning wastes capacity |
| Load shedding | Overload collapsing everything | Priority classes | Must know what's low-priority |
| Graceful degradation | All-or-nothing failure | Fallback path | Fallback must be truly cheap |