Reliability, Timeouts, Retries and Resilience
Design APIs that behave predictably when dependencies fail.
Timeouts
Every network dependency should have an intentional timeout.
Without timeouts, a slow dependency can consume application threads, event-loop resources, connection pools, and memory until the entire service becomes unavailable.
Timeouts should reflect realistic service-level expectations rather than being arbitrarily large.
Different timeout categories can exist for connection establishment, request headers, response body, database operations, and total request duration.
- No network call should wait forever.
- Timeouts prevent resource exhaustion.
- Timeout values should be based on measured service expectations.
Retries and Exponential Backoff
Retries can recover from transient failures but can also make outages worse.
Only retry operations when doing so is safe. Idempotent operations are easier to retry. Non-idempotent operations may require idempotency keys or another deduplication mechanism.
Use bounded retries and exponential backoff with jitter. Retrying immediately from thousands of clients can create a retry storm exactly when the dependency is already unhealthy.
Circuit Breakers and Bulkheads
Resilience Patterns
A circuit breaker stops repeatedly calling a failing dependency after failures cross a threshold. It enters an open state, allowing the dependency time to recover, then gradually probes it again.
Bulkheads isolate resource pools so one dependency cannot consume all available resources.
For example, a service may maintain separate concurrency limits for payment APIs and analytics APIs. A failing analytics service should not prevent payment requests from being processed.
Stop waitingRepeat transient failuresStop calling unhealthy dependencyIsolate resources