← experimental
Contents
  1. Definition
  2. Why It Matters
  3. The Stability Pattern Set
  4. Failure-arresting patterns (catch the cascade)
  5. Load-handling patterns (shape the input)
  6. Defensive patterns (prepare for failure)
  7. Architectural patterns (avoid the failure mode)
  8. How the Patterns Compose
  9. The Antipatterns They Address
  10. How Different Sources Treat It
  11. Related Concepts
  12. Key Quotes

Definition

Stability patterns are a coherent set of design patterns whose purpose is to prevent small failures from cascading into system-wide outages. The term and most of the catalogue come from Release It ch. 5. Each pattern is a "crackstopper" — it interrupts the propagation of fault → error → failure (Nygard's terminology) at a specific architectural boundary. The patterns are composable; a production-grade system uses several together.

Why It Matters

Failure in a complex system isn't a single event — it's a propagation. A slow downstream service blocks a calling thread; blocked threads exhaust a connection pool; pool exhaustion causes upstream callers to block; the chain reaction terminates in cascading failure. Without explicit stability patterns at each layer, every dependency becomes a vector for outage propagation.

Release It frames the problem as crack propagation in a brittle structure. Stability patterns are the structural reinforcements that arrest the cracks. The catalogue is roughly the union of the "design for production" tradition (Nygard, Allspaw, SRE) — practitioner knowledge accumulated from real outages, codified as named patterns.

The Stability Pattern Set

Patterns with their own dedicated pages in this wiki are linked; the rest are summarised here for completeness. The dedicated pages have deeper treatment; this page is the navigation hub and shows how they compose.

Failure-arresting patterns (catch the cascade)

Timeout — bound every wait

Every blocking call (network I/O, lock acquisition, connection-pool checkout) must have a timeout. Without timeouts, a single slow downstream blocks the calling thread indefinitely — the dominant failure mode in production systems (Release It ch. 4: blocked threads). Sizing rule from Release It: derive from the dependency's P99.9 latency distribution.

Circuit Breaker — stop calling a broken dependency

State machine (closed/open/half-open) that suspends calls to a failing dependency, preventing repeated failed calls from amplifying the problem. From Release It: track fault density via a leaky bucket, not absolute count; log state changes for operations; scope is per-process.

Bulkhead — isolate failure domains

Partition resources (thread pools, connection pools, instances) so failure in one partition cannot starve others. Cloud availability zones are infrastructure-level bulkheads. From Release It: reserve a thread pool for critical callers (monitoring, admin) so that a flood of low-priority requests cannot starve operations that must succeed.

Load-handling patterns (shape the input)

Shed Load (and Rate Limiting)

When overloaded, refuse new requests rather than accepting them and failing slowly. Return 503 immediately; signal to the load balancer via health check. From Release It: "The cost of shedding a request is far lower than the cost of degraded service for all concurrent requests. Applied at the service edge."

Backpressure — propagate slowdown upstream

Within a system boundary, use bounded queues to create natural flow control. When a queue is full, the producer blocks — this is the signal to slow down. Distinct from load shedding (backpressure is internal; shed load is at the boundary). Unbounded queues mask the backlog and produce unbounded latency.

Handshaking — server signals readiness

Let the server tell callers whether it can accept work. HTTP: return 503 to the load balancer when health check is failing. From Release It: "an underused pattern — most systems accept connections until they are completely overwhelmed." Circuit Breaker is a stopgap when handshaking is not available downstream.

Defensive patterns (prepare for failure)

Fail Fast — refuse requests we can't handle

Check resource availability before beginning work. If a required circuit breaker is open or a connection pool is exhausted, return an error immediately without consuming resources. Nygard's "mise en place." Distinguish from Timeout: Fail Fast applies to incoming requests (don't start work you can't finish); Timeout applies to outgoing requests (bound your wait). Also distinguish system failures (503) from application failures (400).

Let It Crash — restart from clean state

Borrowed from Erlang. Rather than catching every error, crash the process and let a supervisor restart from known-good state. Critical constraints: granularity must be small (an actor or container, not a whole JavaEE app server); restart must be fast; supervision must exist before startup; reintegration via circuit breaker or health checks to avoid re-adding a not-yet-ready process. From Release It: not appropriate when restart takes minutes — use fault-tolerant recovery instead.

Steady State — no human presence in production

Every mechanism that accumulates state must have a paired mechanism that recycles it. Purge old data programmatically; rotate logs and ship them off-server; bound all in-memory caches. "Fiddling with production" — SSHing in to clear caches or delete rows — is an availability risk. From Release It: automate all steady-state maintenance.

Retry — recover from transient failures

Retry transient failures with exponential backoff and jitter. Prerequisite: idempotency (→ Idempotency). Anti-pattern: retries that don't distinguish transient from permanent errors amplify cascades. Anti-pattern: retries without backoff/jitter create thundering herds.

Architectural patterns (avoid the failure mode)

Decoupling Middleware — async over sync

Choose between synchronous call-and-response (REST, RPC, gRPC) and asynchronous message-oriented middleware (Kafka, RabbitMQ, SQS) with awareness of structural consequences. Synchronous chains propagate backpressure and cascading failures; async middleware decouples in space and time. From Release It: high switching cost — can't easily be retrofitted. See Sync Vs Async Communication.

Test Harnesses — simulate the unsimulable

A separate server (not a mock) that simulates out-of-spec failure modes real integration tests can't provoke: slow responses, garbled bytes, RST mid-stream, connection refused. From Release It: "Mocks verify 'does my code handle what I expect?'; test harnesses verify 'does my code handle what I can't expect?'" Foundation for chaos engineering applied to integration points (→ Chaos Engineering).

Governor — rate-limit automation

Automation (autoscalers, deployment systems, config management) can act on incorrect observations and amplify failures faster than humans can intervene. A Governor limits the rate of change asymmetrically: slow to make large changes, fast to detect alerts. The Reddit ZooKeeper incident (autoscaler shut down the fleet based on partial ZooKeeper data) and AWS S3 outage (typo removed too much capacity) illustrate the need. From Release It ch. 5: U-shaped response curve; act quickly on small safe changes; slow down and require human confirmation for large changes.

How the Patterns Compose

The patterns are not alternatives. A production-grade integration point typically applies all of:

  1. Timeout on the outgoing call
  2. Circuit Breaker to suspend calls when the downstream is failing
  3. Retry with backoff for transient failures
  4. Bulkhead to isolate this dependency's thread pool from others
  5. Fail Fast on incoming requests when the breaker is open
  6. Handshaking with the load balancer via health checks

At the system edge:

  1. Shed Load (return 503) when overloaded
  2. Backpressure internally to slow producers

At the operational layer:

  1. Steady State to prevent resource accumulation
  2. Governor to rate-limit automation
  3. Test Harnesses to validate failure-mode handling

The patterns are at different layers (per-call, per-component, per-system, per-organisation). A system that applies them only at one layer has known failure modes at the others.

The Antipatterns They Address

Release It ch. 4 catalogues the stability antipatterns the patterns above defend against:

  • Integration Points — every integration eventually fails (Timeout + Circuit Breaker + Decoupling Middleware)
  • Chain Reactions — load-related defect propagates across replicas (Bulkhead)
  • Cascading Failures — failure propagates across layers (Circuit Breaker + Timeout)
  • Blocked Threads — the dominant real-world failure mode (Timeout + proven concurrency primitives)
  • Self-Denial Attacks — system conspires against itself (rate-limited rollouts, no CDN bypass)
  • Scaling Effects — O(n²) communication (pub/sub instead of P2P)
  • Unbalanced Capacities — front-end overwhelms back-end (Circuit Breaker + Handshaking + Bulkhead)
  • Dogpile — synchronised burst from simultaneous restart or cache expiry (clock slew, jitter)
  • Slow Responses — worse than refusal (Fail Fast on response-time threshold)
  • Unbounded Result Sets — query returns more than expected (pagination, LIMIT)

See Common Failure Causes for the failure-mode catalogue. The patterns and antipatterns are duals: each pattern targets specific antipatterns.

How Different Sources Treat It

Source Perspective
Release It Canonical source. Twelve patterns in ch. 5; antipatterns they defend against in ch. 4; both organised around the crack-propagation model of cascading failure.
Understanding Distributed Systems Vitillo treats the subset directly relevant to distributed systems — Circuit Breaker, Bulkhead, Timeout, Retry, Idempotency — with explicit decomposition into resiliency primitives.
Foundations Of Scalable Systems Gorton emphasises the queueing-theoretic mechanisms underlying the patterns: blocked threads, connection pools, utilisation curves, producer-consumer with bounded buffers. The same patterns viewed through the lens of capacity rather than failure.
Site Reliability Engineering Google SRE adds the operational layer — load shedding at concurrent-request thresholds, "roll back first, diagnose second," progressive rollouts as the canary form of stability — with explicit error-budget framing.

Key Quotes

"Design failure modes deliberately. Without designed failure modes, cracks propagate unpredictably." — Release It ch. 3

"Every wait must have a limit." — Release It ch. 5 (Timeout)

"Mocks verify 'does my code handle what I expect?'; test harnesses verify 'does my code handle what I can't expect?'" — Release It ch. 5