Background
Sections
IntroductionRequirements & Problem AnalysisConstraints & AssumptionsEstimation TechniquesFunctional vs Non-Functional RequirementsMoSCoW PrioritizationSystem Design FundamentalsArchitecture DiagramClass DiagramComponent DiagramData Flow Diagram (DFD)ER Diagram (Entity-Relationship Diagram)High Level Design (HLD)Low Level Design (LLD)Sequence DiagramState DiagramUse Case DiagramData StorageDocument StoresFile StorageGraph DatabasesIn-Memory DatabasesKey-Value StoresNewSQLNoSQL DatabasesObject StorageSQL Databases (RDBMS)Time-Series DatabasesWide-Column StoresDatabase ConceptsACID PropertiesCAP TheoremConsistency ModelsIndexingNormalization & DenormalizationReplicationSharding & PartitioningTransactions & Isolation LevelsScalabilityAuto-Scaling & ElasticityConsensus & Leader ElectionLoad BalancingReplication & Read ReplicasSharding & PartitioningVertical vs Horizontal ScalingAvailability & ReliabilityBackup & Data DurabilityCircuit BreakerData ConsistencyDisaster RecoveryFault Tolerance & FailoverGraceful DegradationHigh AvailabilityNetworkingCDNDNSFirewalls & VPNHTTP & HTTPSLoad Balancer & Reverse ProxyTCP/IP & UDPWebSocketsCachingCache InvalidationCache Read/Write PatternsCaching LayersEviction PoliciesRedis vs MemcachedMessaging & CommunicationDead-Letter QueueIdempotencyKafka vs RabbitMQ vs SQSMessage QueuesPub/SubCompute & ServicesAPI GatewayContainers & OrchestrationMonolith vs MicroservicesServerlessService DiscoveryService MeshWeb Server & App ServerAPI DesignAPI Versioning & IdempotencyAuthentication & AuthorizationGraphQLgRPCPaginationRate Limiting & ThrottlingRESTSecurityAuthentication & AuthorizationData PrivacyEncryptionInput Validation & InjectionOAuth2 & JWTSecrets ManagementXSS & CSRFStorage & File SystemsBackup & RetentionBlock vs File vs Object StorageData Lakes & WarehousesDistributed File SystemsEphemeral StorageObservability & MonitoringDistributed TracingHealth ChecksLoggingMetricsSLI, SLO, SLADesign PatternsBulkhead PatternCircuit Breaker PatternCreational PatternsRate Limiter PatternRetry PatternStructural & Behavioral Patterns

Design Patterns

6 min read

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