The Circuit Breaker Pattern: failing fast so your system doesn't fail slow
In microservices, a struggling dependency can take the whole system down with it — the circuit breaker pattern stops that spread before it starts.
Microservices call each other constantly. The circuit breaker pattern exists because sooner or later, one of those calls will fail — and the real question is how far that failure is allowed to spread.
The problem it solves
When a downstream service slows down or fails, callers pile up waiting on it: threads, connections, memory. Retrying blindly can make this worse, since a service that cannot recover just gets hammered harder. Sometimes the healthiest move is to fail fast rather than wait for certain failure.
Three states, one state machine
- Closed — requests flow through normally, and failures are tracked over a rolling window.
- Open — once failures cross a threshold, requests stop reaching the service immediately, for a set duration.
- Half-open — after that duration, a limited number of test requests go through. Success closes the breaker again; failure reopens it.
Fallbacks make failure survivable
An open breaker without a fallback just replaces one error with another. A fallback returns a default or degraded response instead, so the caller — and the end user — gets something usable rather than a raw failure.
One breaker per dependency, not per method
Multiple methods calling the same downstream service should share one circuit breaker instance, since what's being measured is the health of that service, not of any single method. Each method can still define its own fallback, tailored to what it does.
Async messaging is a different tool
Queues like Kafka decouple caller and callee so a slow service doesn't block the request — but they don't remove failure, they relocate it, into consumer lag or backpressure. Async fits calls that don't need an immediate answer; synchronous calls that do still need a circuit breaker.
In Spring Boot
Resilience4j is the standard choice, applied through @CircuitBreaker(name, fallbackMethod) and tuned via configuration: sliding window size, failure rate threshold, wait duration in the open state, and how many calls are allowed through in half-open. It pairs well with @Retry, @RateLimiter, and @Bulkhead for a fuller resilience strategy.
The lesson
A circuit breaker doesn't prevent failure. It contains it — turning one unhealthy dependency into a handled case instead of a system-wide outage.