What Is the Circuit Breaker Pattern?
Your app calls a payment service. One day, that service gets slow: every request hangs for 30 seconds before failing. Your app keeps calling it. Threads pile up waiting, memory fills, and soon your app is down too, even though the problem was somewhere else.
A circuit breaker prevents this. Like the electrical breaker in your home, it trips when something goes wrong and stops the flow, so one failing service can't take down everything connected to it.
🎯 Quick Answer (30-Second Read)
- A circuit breaker wraps calls to another service and watches for failures.
- Closed: calls flow normally while failures are counted.
- Open: after too many failures, calls fail immediately without touching the broken service.
- Half-open: after a cool-down, a few test calls go through. Success closes the circuit, failure opens it again.
- Pair it with timeouts, retries (carefully) and fallbacks. Popular libraries: Resilience4j (Java), Polly (.NET), pybreaker (Python). Netflix's Hystrix popularised the pattern but is now in maintenance mode.
The Problem: Cascading Failures
🐢 slow / failing"] A --> C["Inventory service"] style B fill:#fee2e2,stroke:#ef4444
Without protection:
- Payment slows down.
- Checkout's threads all wait on payment.
- Checkout stops responding, even for requests that don't need payment.
- Services calling checkout start failing too.
That chain reaction is a cascading failure. Retrying makes it worse, because a struggling service now gets more traffic.
The Three States
(e.g. 50% of last 20 calls) Open --> HalfOpen: wait duration passes
(e.g. 30s) HalfOpen --> Closed: test calls succeed HalfOpen --> Open: a test call fails note right of Open Calls fail fast, fallback is used end note
| State | What happens to calls | Purpose |
|---|---|---|
| Closed | Pass through, results recorded | Normal operation |
| Open | Rejected instantly (or fallback returned) | Give the failing service room to recover, protect your resources |
| Half-open | A few test calls allowed | Check whether the service has recovered |
Fallbacks: What to Do When the Circuit Is Open
Failing fast is better than hanging, but a good fallback is better still:
Show the last known product recommendations instead of fresh ones.
Show a generic list or "delivery estimate unavailable."
Accept the order and process the step when the service recovers.
This is graceful degradation: the core experience survives while a non-essential feature is temporarily reduced.
Settings You'll Tune
| Setting | Example | Effect |
|---|---|---|
| Failure rate threshold | 50% | How bad things must be to trip |
| Window size | Last 20 calls or last 60s | How much history to judge |
| Minimum calls | 10 | Avoid tripping on 1 failure out of 2 |
| Slow call threshold | > 2s counts as failure | Treat slowness like failure |
| Wait in open state | 30s | How long before testing recovery |
| Calls in half-open | 3 | How many test calls to allow |
Example: Resilience4j (Java)
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slowCallDurationThreshold(Duration.ofSeconds(2))
.slidingWindowSize(20)
.minimumNumberOfCalls(10)
.waitDurationInOpenState(Duration.ofSeconds(30))
.permittedNumberOfCallsInHalfOpenState(3)
.build();
CircuitBreaker breaker = CircuitBreaker.of("payments", config);
Supplier call = CircuitBreaker.decorateSupplier(breaker, paymentClient::charge);
String result = Try.ofSupplier(call)
.recover(ex -> "PAYMENT_QUEUED") // fallback
.get(); Example: A Minimal Breaker in Python
import time
class CircuitBreaker:
def __init__(self, max_failures=5, reset_after=30):
self.max_failures, self.reset_after = max_failures, reset_after
self.failures, self.opened_at = 0, None
def call(self, fn, *args, fallback=None):
if self.opened_at:
if time.time() - self.opened_at < self.reset_after:
return fallback # OPEN: fail fast
self.opened_at = None # HALF-OPEN: try once
try:
result = fn(*args)
self.failures = 0 # success → CLOSED
return result
except Exception:
self.failures += 1
if self.failures >= self.max_failures:
self.opened_at = time.time() # trip → OPEN
return fallbackThis sketch counts consecutive failures. Production libraries use sliding windows, slow-call detection and thread safety.
Circuit Breaker vs Retry vs Timeout
Frequently Asked Questions
What is the half-open state in a circuit breaker?
After the circuit has been open for a set time, it moves to half-open and lets a small number of test calls through. If they succeed, the service is considered healthy and the circuit closes. If any fail, it opens again and waits another cool-down period.
Is Hystrix still used?
Netflix Hystrix made the circuit breaker pattern popular in microservices, but it has been in maintenance mode since 2018. Netflix recommended alternatives such as Resilience4j for new projects, and service meshes like Istio also offer circuit breaking.
Where should a circuit breaker live?
Usually in the calling service, around each call to an external dependency, or in infrastructure such as an API gateway or service mesh sidecar. Use separate breakers per dependency, so one failing service doesn't block calls to healthy ones.
Conclusion
The circuit breaker pattern stops cascading failures by failing fast when a dependency is unhealthy, then testing carefully for recovery. Combine it with timeouts, limited retries and sensible fallbacks, and a single slow service becomes a degraded feature instead of a full outage.
Sources
- Martin Fowler: CircuitBreaker
- Resilience4j documentation: CircuitBreaker
- Netflix Hystrix on GitHub (maintenance mode notice)
Last verified: 14 October 2026.
Related reads: Why Netflix Rarely Goes Down · Microservices Architecture Explained · Rate Limiter Explained · Why Apps Crash During High Traffic
I write about system design and how real products are built. Follow on LinkedIn or X, or invite me to speak.