Resilience Patterns

Keeping one failure from becoming an outage. Timeouts, retries, circuit breakers, bulkheads โ€” and the cascade they exist to stop.

Timeouts Retries + backoff Circuit breaker Bulkheads Load shedding
๐Ÿ›ก

Failure Is the Default

With 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

Every 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.

๐Ÿ’ก
Propagate deadlines. If the top-level request has 3 seconds, a call three levels deep shouldn't get a fresh 3-second budget โ€” it inherits what's left. Pass the deadline down the chain so nobody works on a request the client already gave up on. "Every remote call has a timeout, and I propagate the deadline" is a cheap, senior line.
๐Ÿ”

Retries

Retries 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
โš ๏ธ
Retries are dangerous precisely when a service is already struggling โ€” that's when everyone retries at once. Exponential backoff spaces attempts out; jitter (randomness) desynchronizes clients so they don't retry in lockstep. And beware retry amplification: if every layer retries 3ร—, three stacked layers turn one request into 27 against the bottom service. Use a retry budget โ€” cap retries as a fraction of traffic โ€” and retry at one layer, not every layer.
๐Ÿ”Œ

Circuit Breaker

When 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

Named 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
โ„น๏ธ
Without isolation, a single slow dependency is enough to hang an entire service โ€” every incoming request eventually blocks on the one slow call and the shared pool drains. Bulkheads cap that damage to the one dependency's share. This is the containment step that stops the cascade below.
๐Ÿ“‰

Load Shedding & Graceful Degradation

Under 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.

๐Ÿ’ก
Feature flags as kill switches make this operational: when the recommendation service is melting, flip a flag to serve a generic list and keep the page loading. The product degrades a little; it doesn't go down. "Under load I'd shed the personalization and serve a cached default rather than fail the whole request" is exactly the degradation instinct interviewers want to hear.
๐ŸŒฉ

Cascading Failure Anatomy

Cascading 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
โš ๏ธ
The vicious part is step 5: removing an overloaded node moves its load onto the survivors, pushing them over the same edge โ€” the failure accelerates as it spreads. A deep health check makes it worse by taking every node out at once when the shared DB blips (Discovery). The patterns aren't academic; each one severs a specific link in this exact chain.
๐Ÿ“‹

Quick Reference

PatternProtects againstKey parameterGotcha
TimeoutHung calls exhausting resourcesDeadline (propagated)Missing one turns slow into down
RetryTransient blipsBackoff, jitter, budgetAmplifies load if stacked
Circuit breakerHammering a downed dependencyFailure threshold, cooldownTune so it doesn't flap
BulkheadOne dependency draining all resourcesPool size per dependencyOver-partitioning wastes capacity
Load sheddingOverload collapsing everythingPriority classesMust know what's low-priority
Graceful degradationAll-or-nothing failureFallback pathFallback must be truly cheap
โ„น๏ธ
The one-liners to keep ready. Failure is the default; contain, degrade, recover. Every remote call gets a timeout with a propagated deadline. Retry only transient, idempotent calls, with backoff and jitter, at one layer. Circuit breakers fail fast to protect both sides. Bulkheads isolate the blast radius. Shed low-priority load and degrade before you fail. Each pattern cuts a specific link in the cascade chain.