Engineering Notes
Home/Advanced Engineering/Advanced/Resilience and Reliability Patterns
Advanced Engineering · Advanced

Resilience and Reliability Patterns

Assume everything fails. Combine timeouts, retries, breakers, bulkheads, load shedding and graceful degradation into a coherent toolbox, step down a degradation ladder during an incident, and verify it all with chaos engineering.

🚀 Advanced Engineering● Advanced⏱ 5 min read▶ 2 interactive🛠️ 2 exercises🎤 5 interview Q&As

🎯 What you'll learn

  • Design assumptions for distributed failure
  • The pattern toolbox and how patterns interact
  • Bulkheads, load shedding and prioritization
  • Graceful degradation ladders and chaos engineering

Reliability is the probability a system does what users need, when they need it. Resilience is how it behaves when parts fail — and in distributed systems parts always fail. This topic collects the patterns into one toolbox and shows how they combine.

1Design Assumptions

  • Every network call can fail, be slow, or succeed without you knowing (06-failure_handling_and_retry_strategies/).
  • Dependencies will be down at some point; your service's availability is bounded by synchronous dependencies (availability of a chain ≈ product of availabilities).
  • Load will exceed capacity at some point (spikes, retries, bugs).
  • Humans will deploy bad changes.

The goal: contain failures (blast radius), degrade gracefully, and recover automatically.

2The Pattern Toolbox

PatternProblem it addressesMechanism
timeoutsslow dependencies hold resources foreverbound every wait
retries with backoff + jittertransient failuresbounded, idempotent, one layer
circuit breakerrepeatedly calling a dead dependencyfail fast while open; probe to recover (distributed-systems/fundamentals/07-circuit_breaker)
bulkheadone dependency/tenant exhausting shared resourcesseparate pools/queues/concurrency limits per dependency or tenant
rate limitingclients overloading youtoken buckets per client (backend/api-design/07-rate_limiting)
load sheddingoverload of the whole servicereject early (429/503) when queues/latency exceed thresholds; prioritize critical traffic
backpressurefast producers, slow consumersbounded queues, pull-based consumption (distributed-systems/fundamentals/05-backpressure)
fallback / graceful degradationdependency downcached/stale data, defaults, feature turned off
redundancy & failoverinstance/AZ/region failureN+1 instances, multi-AZ, active-active/passive (distributed-systems/fundamentals/10-…)
idempotencyretries and duplicatesidempotency keys, dedup, upserts
queue-based load levelingspiky loadbuffer work in a durable queue
health checks & self-healingstuck instancesliveness/readiness, auto-replacement (devops-cloud/observability/04-health_checks)
static stabilitycontrol plane downdata plane keeps working on last-known config
safe deploymentsbad changescanaries, feature flags, automatic rollback (devops-cloud/cicd/02-deployment_strategies)

3Bulkheads

Named after ship compartments: a breach floods one compartment, not the ship.

ts
const pools = {
  payments: new Semaphore(20),         // at most 20 concurrent calls to payments
  recommendations: new Semaphore(5),   // a slow recommender can tie up at most 5 slots
};

async function callRecommendations(userId: string) {
  if (!pools.recommendations.tryAcquire()) return FALLBACK_RECOMMENDATIONS;   // full → degrade, don't queue
  try { return await withTimeout(recoClient.get(userId), 200); }
  catch { return FALLBACK_RECOMMENDATIONS; }
  finally { pools.recommendations.release(); }
}

Without the bulkhead, a slow recommendation service consumes every worker/connection, and checkout (which needs payments) fails too.

4Load Shedding and Prioritization

When demand exceeds capacity, somebody won't be served. Decide who, deliberately:

  • reject early and cheaply (before parsing bodies or touching the DB),
  • use queue time or in-flight count as the signal (concurrency limits like Netflix's adaptive limiters),
  • prioritize: checkout > browsing > analytics beacons; authenticated > anonymous; interactive > batch,
  • return 503/429 with Retry-After so well-behaved clients back off.

Serving 80% of requests well beats serving 100% of requests so slowly that they all time out (goodput vs throughput).

5Graceful Degradation Ladder

Plan what the product does as dependencies fail:

text
all healthy          → full experience
recommendations down → show bestsellers (static/cached)
search down          → category browsing only
payments slow        → accept order, "payment processing" state, confirm asynchronously
inventory down       → allow orders for items with stale stock > threshold, reconcile later
database read replica down → serve reads from primary with stricter rate limits

Each step needs a feature flag / kill switch and is exercised in tests and game days.

6Verifying Resilience: Chaos Engineering

Untested resilience mechanisms usually don't work. Chaos engineering runs controlled experiments: hypothesize steady state ("checkout success ≥ 99.5%"), inject a failure (kill pods, add 500 ms latency to payments, black-hole the cache, fail an AZ), observe, and fix what breaks. Start in staging, with small blast radius and an abort switch; graduate to production game days. Tools: Chaos Mesh, Litmus, AWS FIS, Gremlin, Toxiproxy (for local tests).

Under the HoodWhy Resilience Mechanisms Interact▾

Patterns compose — and can fight. A retry policy inside a circuit breaker: should failed retries count as one failure or several? (Usually the breaker wraps the whole retried call, so persistent failure opens it quickly.) A timeout shorter than the dependency's p99 turns slow-but-successful calls into failures that trigger retries, increasing load on the slow dependency (metastable failure: the system stays degraded even after the original trigger disappears, because retries and timeouts sustain the overload). Load shedding and autoscaling interact: shedding hides demand from CPU-based scaling signals. The safe composition order from the caller's view is usually: bulkhead (concurrency limit) → circuit breaker → timeout → retry (budgeted) → fallback, with metrics on each stage so you can see which one acted.

A call passes through a bulkhead concurrency limit, a circuit breaker, a timeout and a budgeted retry before reaching the dependency, with a fallback when any stage gives up; load shedding at the edge and a degradation ladder keep critical flows working
A call passes through a bulkhead concurrency limit, a circuit breaker, a timeout and a budgeted retry before reaching the dependency, with a fallback when any stage gives up; load shedding at the edge and a degradation ladder keep critical flows working

⚠️ Common Pitfalls

  1. Resilience features that are never tested — fallbacks that throw, breakers that never open.
  2. Unbounded queues "for resilience" — latency explodes, then memory.
  3. Shared pools for critical and non-critical dependencies.
  4. Timeouts longer than the caller's timeout.
  5. Retries + short timeouts on an overloaded dependency — metastable failure.
  6. Fallbacks that hit the same failing dependency (e.g. "fallback" to the same DB).

📋 Quick Reference

text
contain: bulkheads · rate limits · load shedding · backpressure
detect & stop: timeouts · circuit breakers · health checks
recover: retries (budgeted, idempotent) · failover · self-healing · safe deploys
degrade: fallbacks · degradation ladder · kill switches
verify: chaos experiments · game days
order: bulkhead → breaker → timeout → retry → fallback

🧠 Key takeaways

  • Assume partial failure: every dependency can be slow, wrong or down.
  • Toolbox: timeouts, retries, circuit breakers, bulkheads, load shedding, caching, fallbacks, which interact (tune them together).
  • Shed load by priority (checkout before recommendations) before the system collapses.
  • Pre-build a degradation ladder and verify everything with chaos experiments and game days.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1Name the key resilience patterns.core▾

Timeouts, retries with backoff and jitter, circuit breakers, bulkheads, rate limiting and load shedding, fallbacks/graceful degradation, idempotency, health checks with automatic failover, and queue-based load leveling.

Follow-ups interviewers ask
  • Which is the most important? Timeouts: without them every other pattern fails.
Q2What is load shedding?core▾

Deliberately rejecting some requests when overloaded (fast 503/429) to keep serving the rest within latency targets, instead of slowing down for everyone. Prioritize critical traffic (checkout over recommendations).

Follow-ups interviewers ask
  • How do you decide what to shed? Request priority classes and concurrency or latency thresholds.
Q3What is graceful degradation? Give examples.core▾

Continuing to provide core functionality when parts fail: showing cached prices with a notice, hiding recommendations if that service is down, queuing orders for later processing, or switching to read-only mode during a database failover.

Follow-ups interviewers ask
  • How do you implement it? Feature flags, fallbacks behind circuit breakers, and clear UI states.
Q4What is chaos engineering?senior▾

Running controlled experiments that inject failures (killing instances, adding latency, dropping network) to verify the system behaves as expected and to find weaknesses before real incidents. Start small, in staging, with a hypothesis and a way to abort.

Follow-ups interviewers ask
  • Famous example? Netflix's Chaos Monkey.
Q5How do you measure reliability?core▾

With SLIs and SLOs (availability, latency, correctness), error budgets, MTTD/MTTR, incident frequency and severity. Reliability work is prioritized when the error budget is burning.

Follow-ups interviewers ask
  • What's MTTR? Mean time to restore service after a failure.

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. Under overload, what should be shed first?
Protect critical paths.
2. Why tune timeouts, retries and breakers together?
Resilience mechanisms interact.
3. Chaos engineering means…
Start small, with a blast radius.
4. A degradation ladder should be…
Practice before you need it.