Resilience Patterns
Resiliency patterns that isolate failures, absorb load, and preserve service.
Resiliency patterns exist to stop a local problem from turning into a system-wide outage. Distributed systems fail in ordinary ways: dependencies become slow, queues build up, downstream services reject traffic, and one noisy tenant consumes too many shared resources. Good resiliency design does not assume failure is rare. It limits blast radius when failure arrives.
Timeouts prevent resource capture
A timeout defines the maximum time a caller is willing to wait for a dependency. This sounds basic, but without it threads, connection pools, and worker slots can become trapped behind a slow dependency until the calling service fails as well. Timeouts are one of the cheapest ways to prevent cascading failure.
The hard part is setting them realistically. Too short and healthy calls fail needlessly. Too long and the timeout no longer protects capacity.
Retries recover transient faults, but can also cause them
Retries are useful when failures are temporary, such as a brief network glitch or a cold dependency. They become dangerous when every caller retries aggressively at once. A failing dependency can be buried under retry storms from well-meaning clients.
That is why good retries have limits, exponential backoff, and jitter. Jitter matters because it spreads attempts out instead of aligning them.
Circuit breakers defend overloaded dependencies
A circuit breaker observes failure rate or latency and temporarily stops sending requests to a dependency that is already in trouble. This gives the downstream service space to recover and stops the caller from wasting work on almost-certain failure.
Circuit breakers are not a substitute for fixing the root cause. They are a containment tool.
Bulkheads isolate shared resources
Bulkheads partition resources such as thread pools, queues, or connection pools so one failing or high-volume workflow cannot consume everything. This is especially important in multi-tenant systems or services that call many dependencies with different reliability characteristics.
Without isolation, one bad dependency can take the whole service down indirectly.
Rate limiting, load shedding, and back pressure
Rate limiting protects a system from unbounded client demand. Load shedding drops work intentionally when the service is near saturation, usually sacrificing low-priority requests to preserve core paths. Back pressure tells upstream producers to slow down rather than allowing buffers to grow without limit.
These patterns all accept the same reality: the system cannot serve infinite demand, so it must degrade deliberately instead of collapsing accidentally.
Let it crash can be the right choice
Some components should fail fast and restart cleanly under supervision. If a worker process enters a corrupt state, keeping it alive may be worse than replacing it. This pattern only works if start-up is quick and state recovery is well understood.
Patterns work as a system
The main mistake is treating one resiliency pattern as enough. Retries without timeouts are dangerous. Circuit breakers without fallback or graceful error handling just fail faster. Rate limits without observability lead to mysterious user complaints.
Resiliency is therefore a composition problem. The goal is not zero failure. The goal is graceful degradation, quick recovery, and a small blast radius when something inevitably goes wrong.