Design Patterns
Reusable Solutions to Recurring Problems
Design patterns are named, proven solutions to problems that come up again and again in software design. They exist at two levels that this topic deliberately spans. The classic object-oriented patterns (creational, structural, behavioral) — cataloged by the "Gang of Four" — solve recurring problems in how you create, compose, and coordinate objects within a program. The resilience patterns (circuit breaker, retry, bulkhead, rate limiter) solve recurring problems in how distributed systems survive failure and overload. What unites them is that both are a shared vocabulary: saying "use a strategy here" or "wrap that call in a circuit breaker" communicates an entire, battle-tested design in a few words.
The reason patterns matter is that they encode hard-won experience. The OO patterns capture how to keep code flexible, testable, and extensible as requirements change — decoupling creation from use, composing behavior without rigid hierarchies, coordinating objects without tight coupling. The resilience patterns capture how to keep systems up when dependencies fail, networks glitch, and traffic spikes — and they compose into a coherent defense where each handles a different failure mode. Knowing patterns doesn't just give you solutions; it gives you a way to recognize which problem you're facing and reach for the structure that's already known to work — while also knowing when not to apply one, since forcing patterns where simplicity suffices is its own anti-pattern.
When This Comes Up
- System design interviews: The resilience patterns especially are core interview material — "how do you stop one failing service from taking down the system?" pulls in circuit breakers, bulkheads, retries, and rate limiters, and strong candidates explain how they compose. The OO patterns appear in low-level design (LLD) rounds, where you're asked to design classes for a parking lot, an elevator, or a payment system and expected to apply Factory, Strategy, Observer, and friends appropriately.
- Real architecture: These patterns are the daily building blocks of production code and resilient services. Factories choose implementations by config, strategies make algorithms pluggable, and the resilience patterns are what stand between a minor dependency hiccup and a company-wide outage.
- Code and design reviews: Patterns are the shared language reviewers use — "extract a strategy," "this needs a bulkhead," "add a retry with backoff." Fluency in them makes design discussions faster and clearer, and helps you spot both missing structure and needless over-engineering.
How the Sub-Topics Connect
The sub-topics divide into the classic OO patterns (creational, then structural & behavioral) and the resilience patterns (circuit breaker, retry, bulkhead, rate limiter) that compose into a distributed-systems defense:
1. Creational Patterns
Proven solutions to how objects are created — adding indirection so code depends on abstractions rather than concrete classes. The classics: Singleton (one shared instance — powerful but overused global state, best used deliberately), Factory (decide which class to instantiate, so callers depend on an interface — the open/closed principle in action), Abstract Factory (families of related objects), Builder (assemble complex objects step by step, avoiding telescoping constructors), and Prototype (clone instead of construct). In system design they appear constantly in how you build clients, connection pools, configs, and complex requests — making systems flexible (swap implementations), testable (inject mocks), and extensible (add types without editing callers).
2. Structural & Behavioral Patterns
The other two GoF families. Structural patterns govern how objects are composed — Adapter (bridge incompatible interfaces), Decorator (layer behavior by wrapping), Facade (a simple front to a complex subsystem), Proxy (a controlling stand-in), Composite (uniform trees). Behavioral patterns govern how objects collaborate — Observer (the OO root of pub/sub), Strategy (pluggable interchangeable algorithms), Command (requests as objects), State (behavior changes with state). These connect directly to system-scale ideas: a Decorator is a caching proxy, a Facade is an API gateway, Strategy is a swappable load-balancing policy, Observer is pub/sub — patterns are the same principles at object scale.
3. Circuit Breaker Pattern
A resilience pattern that prevents repeatedly calling a failing dependency — tripping "open" after too many failures to fail fast and give the dependency room to recover. Implemented as a three-state machine (Closed → Open → Half-Open), it's the State pattern applied to resilience. The implementation details that make it work: counting timeouts as failures, using a rolling window, per-dependency breakers, and pairing with timeouts, retries, and a graceful fallback. It turns a failing dependency from a cascading, resource-exhausting outage into a fast, contained, self-healing degradation. (Complements the availability-focused Topic 06 treatment.)
4. Retry Pattern
Handling transient failures by trying again — one of the cheapest, highest-leverage resilience techniques, but a common cause of self-inflicted outages when done naively. The disciplines that make it safe: exponential backoff (thin out retries so you don't hammer a struggling service), jitter (randomize delays to avoid synchronized thundering herds), bounded attempts/retry budgets (never retry infinitely), retrying only transient, retryable errors, and — non-negotiably — only idempotent operations (or made idempotent with keys) so a retry can't double-charge. It composes with the circuit breaker: retry absorbs the blip, the breaker stops the retries once failure is persistent.
5. Bulkhead Pattern
Named after a ship's watertight compartments: isolate resources (thread pools, connection pools, instances) so a failure or overload in one part can't consume everything and cause a cascading outage. Without bulkheads, one slow dependency can exhaust a shared thread pool and starve every other operation; with them, that failure is contained to its own compartment. Bulkheads also enable prioritization — dedicated capacity for critical paths so noisy low-value traffic can't starve them. It addresses resource contention — a failure mode retries and breakers don't fully solve — and composes with them: the bulkhead contains the blast radius while the breaker short-circuits the broken dependency.
6. Rate Limiter Pattern
Controlling the rate of operations, in both directions: server-side to protect your service from clients (abuse, spikes, runaway loops), and client-side to keep your outbound calls within a downstream's limits (avoid being throttled or banned). The token bucket algorithm is the go-to — allowing controlled bursts while enforcing an average rate — and correct distributed enforcement requires shared atomic state (Redis) so a client can't multiply its allowance across servers. It composes with the other resilience patterns: rate limiters cap per-client volume, bulkheads isolate resources, breakers stop calling broken dependencies, retries absorb blips, and load shedding drops low-priority work under overload. (Complements the API-focused Topic 11 treatment.)
Sub-Topics
| # | Sub-Topic | What You'll Learn |
|---|---|---|
| 1 | Creational Patterns | Flexible object creation: Singleton, Factory, Builder, and more |
| 2 | Structural & Behavioral Patterns | Composing and coordinating objects: Decorator, Facade, Strategy, Observer |
| 3 | Circuit Breaker Pattern | Failing fast to stop cascading failures, as a state machine |
| 4 | Retry Pattern | Handling transient failures safely with backoff and jitter |
| 5 | Bulkhead Pattern | Isolating resources to contain failures |
| 6 | Rate Limiter Pattern | Controlling operation rates inbound and outbound |