Skip to content
Back to AI Blog
The Pattern Decision Framework: Choosing the Right Pattern
 Backend

The Pattern Decision Framework: Choosing the Right Pattern


Design Patterns Software Architecture Decision Framework Software Engineering

The hardest part of design patterns is not learning them. It is knowing which one to use --- and when to use none at all.


Table of Contents


The Real Challenge

Over the past nine posts, we have built a comprehensive understanding of software design patterns --- from the foundational GoF patterns to SOLID principles, from creational and structural patterns to behavioral ones, from language-specific adaptations to real-world case studies at Netflix, Google, Amazon, and Uber.

But there is a gap between knowing patterns and applying them well. Every experienced developer has encountered codebases where patterns were applied enthusiastically but poorly --- a Singleton where a simple function would do, a Factory producing exactly one type, an Observer creating memory leaks because nobody considered deregistration.

This post bridges that gap. It is a decision framework: a structured approach to answering the question “which pattern should I use?” --- and the equally important question “should I use a pattern at all?”


Problem-to-Pattern Mapping

The most practical way to choose a pattern is to start with the problem, not the pattern. The following mapping covers 38 common problem scenarios and the patterns that address them.

Creational Problems

“How do I create objects?”

#Problem StatementRecommended Pattern(s)Key Consideration
1”I need to ensure only one instance exists”SingletonConsider Dependency Injection as a modern alternative; Singleton introduces global state and complicates testing
2”I need to create objects without specifying the exact class”Factory MethodUse when object creation depends on runtime input or when adding new types without modifying existing code
3”I need to create families of related objects that must be consistent”Abstract FactoryIdeal for theme systems (dark/light mode), cross-platform UI kits, multi-vendor cloud resources
4”I need to construct complex objects step by step”BuilderUse for objects with many optional fields or complex multi-step configuration (HTTP requests, SQL queries)
5”I need copies of existing objects without coupling to their classes”PrototypeApply when cloning is more efficient than constructing from scratch (document templates, game characters)
6”I need to recycle expensive-to-create objects”Object PoolUse for database connections, thread pools, socket connections where acquisition/release cost is high
7”I need a simple way to create one of several possible classes”Simple Factory (not GoF)Use as a stepping stone; upgrade to Factory Method when extensibility is needed
8”I need to register and dynamically create objects by identifier”Registry / Service LocatorUse when types are discovered at runtime; consider DI containers as a more testable alternative

Structural Problems

“How do I compose objects?”

#Problem StatementRecommended Pattern(s)Key Consideration
9”I need to make incompatible interfaces work together”AdapterWraps an existing class with a new interface; does not add behavior, only translates
10”I need to decouple abstraction from implementation so both can vary”BridgePrevents class explosion in multi-dimensional hierarchies (e.g., shapes x rendering APIs)
11”I need to represent a tree/hierarchy where parts and wholes are treated uniformly”CompositeFile systems, org charts, UI component trees, menu structures
12”I need to add behavior without modifying existing code”DecoratorAdds responsibilities dynamically; stackable (e.g., encryption + compression on a stream)
13”I need to simplify a complex subsystem with a unified interface”FacadeProvides a simplified API; does not prevent direct subsystem access if needed
14”I need to cache/share expensive objects to reduce memory”FlyweightUse when thousands of objects share intrinsic state (text editors, game sprites, map tiles)
15”I need to control access to an object”ProxyVariants: virtual (lazy-load), protection (access control), remote (network), caching
16”I need to restrict accessor/mutator access to object data”Private Class DataReduces exposure of internal state; limits write access after construction

Behavioral Problems

“How do objects communicate and divide responsibility?”

#Problem StatementRecommended Pattern(s)Key Consideration
17”I need to notify multiple objects of state changes”ObserverEvent-driven systems, UI updates, reactive streams; beware performance with many observers
18”I need to switch algorithms at runtime”StrategyEncapsulates interchangeable algorithms behind a common interface (sorting, compression, payment methods)
19”I need to encapsulate a request as an object”CommandEnables queuing, logging, undo/redo, macro recording, and transactional operations
20”I need to change an object’s behavior when its internal state changes”StateReplaces complex conditional logic with state objects (order lifecycles, media player states)
21”I need to add operations to a class hierarchy without modifying it”VisitorDouble-dispatch mechanism; ideal for compilers, AST walkers, data export tools
22”I need to save and restore object state”MementoCaptures snapshots without breaking encapsulation; game saves, editor undo, checkpoint systems
23”I need to traverse a collection without exposing internals”IteratorStandard in most language collection libraries; supports multiple concurrent traversals
24”I need to reduce complex communication between many objects”MediatorCentralizes interaction logic; chatrooms, air traffic control, UI dialog coordination
25”I need to process a request through multiple handlers”Chain of ResponsibilityServlet filters, middleware pipelines, approval workflows, logging chains
26”I need to define a skeleton algorithm with customizable steps”Template MethodFixed algorithm structure, variable steps via subclass overrides; document parsers, test frameworks
27”I need to support undo/redo operations”Command + MementoCommand captures the operation; Memento captures the state snapshot before execution
28”I need to interpret or evaluate a language/grammar”InterpreterUse for DSLs, search filters, chatbot rule engines, mathematical expression parsers
29”I need to handle null gracefully without null checks everywhere”Null ObjectProvides a do-nothing default implementation; eliminates defensive null checking
30”I need to define complex boolean business rules composably”SpecificationCombine rules with AND/OR/NOT; ideal for filtering, validation, and query criteria

Cross-Cutting and Architectural Problems

#Problem StatementRecommended Pattern(s)Key Consideration
31”I need to add logging/security/caching without polluting business logic”Interceptor / AOPAspect-oriented approach; pre/post processing via proxies or framework interceptors
32”I need to decouple event producers from consumers”Publish-Subscribe (Observer variant)Message brokers, event buses; stronger decoupling than basic Observer
33”I need to coordinate multiple data operations as a single transaction”Unit of WorkTracks changes and applies them atomically (e.g., Entity Framework’s SaveChanges)
34”I need to abstract data access behind a collection-like interface”RepositoryEncapsulates query logic; promotes domain-driven design; testable via in-memory implementations
35”I need to map between domain objects and database rows”Data MapperSeparates domain model from persistence schema; use when domain and DB diverge significantly
36”I need to provide a fluent, readable API for configuration”Builder + Fluent InterfaceMethod chaining for readability (e.g., query.select("name").from("users").where("active"))
37”I need to separate read and write models for scalability”CQRSCommand-Query Responsibility Segregation; often paired with Event Sourcing
38”I need to reconstruct state from a sequence of events”Event SourcingStore events rather than state; enables audit trails, temporal queries, and replay

Pattern Relationship Map

Design patterns rarely exist in isolation. Their real power emerges when combined. The relationships below are based on the classifications from Zimmer (1995) and the GoF book: “X uses Y” (Y is part of X’s solution), “X is similar to Y” (solve related problems), and “X can be combined with Y” (synergistic pairing).

Creational Pattern Combinations

CombinationHow They Work TogetherReal-World Example
Factory + SingletonFactory method returns a singleton instance; factory itself may be a singletonLogging framework: LoggerFactory.getLogger() returns the shared logger instance
Abstract Factory + PrototypeFactory clones prototypical instances instead of calling constructorsUI toolkit where theme factories clone pre-configured widget prototypes
Builder + Fluent InterfaceBuilder exposes chainable methods for step-by-step constructionHttpRequest.newBuilder().uri(uri).header("Accept","json").GET().build()
Factory + StrategyFactory creates the appropriate strategy based on contextPayment processor factory that returns Stripe/PayPal/Braintree strategy based on region
Factory + BuilderFactory selects which builder to use; builder constructs the objectReport generator factory selects PDF/HTML/CSV builder based on export format
Abstract Factory + Factory MethodAbstract Factory uses Factory Methods internally to create each productCross-platform GUI factory with createButton(), createCheckbox() factory methods

Structural Pattern Combinations

CombinationHow They Work TogetherReal-World Example
Decorator + Chain of ResponsibilityDecorators form a processing pipeline where each can pass to the nextHTTP middleware stack: auth decorator -> logging decorator -> compression decorator
Adapter + FacadeFacade simplifies the interface; Adapter converts between incompatible interfaces behind itAPI gateway that provides a unified facade while adapting multiple microservice protocols
Composite + IteratorIterator traverses Composite’s tree structure uniformlyFile system walker iterating over directories (composites) and files (leaves)
Proxy + DecoratorBoth wrap an object; Proxy controls access, Decorator adds behaviorCaching proxy that also decorates responses with timing metadata
Bridge + AdapterBridge separates abstraction/implementation; Adapter bridges the gap to legacy interfacesMessaging system where Bridge separates message type from delivery channel, with Adapters for legacy protocols
Composite + VisitorVisitor traverses the Composite tree, performing operations at each nodeAST (Abstract Syntax Tree) in a compiler: Composite tree structure walked by type-checker Visitor

Behavioral Pattern Combinations

CombinationHow They Work TogetherReal-World Example
Command + MementoCommand executes the action; Memento stores state for undoText editor: each edit is a Command, with Memento snapshots enabling Ctrl+Z
Observer + MediatorMediator coordinates communication; Observer notifies of changesChat application: Mediator manages room logic, Observer pattern notifies connected clients
Strategy + Template MethodTemplate Method defines skeleton; Strategy handles variable algorithm stepsData processing pipeline: template defines extract-transform-load flow; strategy varies the transform step
State + StrategyState switches strategies based on internal state transitionsGame character: State determines current mode (idle/combat/stealth); Strategy determines behavior within that mode
Iterator + VisitorIterator traverses the collection; Visitor performs operations on each elementDatabase result set: Iterator moves through rows, Visitor serializes each to JSON/XML
Command + ObserverCommand executes actions; Observer is notified of command executionGUI framework: button click fires Command; Observer updates toolbar state and status bar
Chain of Responsibility + CommandCommands are passed down a chain of handlers for processingEvent system: input events as Commands processed by a chain of UI handlers (bubbling)

Cross-Category Combinations

CombinationHow They Work TogetherReal-World Example
Factory + ObserverFactory creates observers and registers them with subjectsPlugin system: factory creates plugin instances and subscribes them to relevant events
Singleton + MediatorMediator is typically a singleton coordinating system-wide communicationEvent bus: single mediator instance routing messages between all application components
Proxy + ObserverProxy intercepts access and notifies observers of changesData-binding frameworks: proxy on model objects triggers UI updates via Observer
Builder + CompositeBuilder constructs a Composite tree structure step by stepHTML DOM builder that constructs nested element hierarchies fluently
Factory + DecoratorFactory assembles the decorator chain based on configurationI/O stream factory that wraps base stream with buffering, encryption, and compression decorators

Pattern Selection by Architecture Layer

Where you are in the architecture stack changes which patterns are most relevant. This section maps patterns to the layer where they provide the most value.

Presentation Layer

Handles UI rendering, user interaction, and display logic.

PatternRole in This LayerWhen to Choose
MVC (Model-View-Controller)Separates display, input handling, and dataWeb applications with server-rendered views
MVP (Model-View-Presenter)View is passive; presenter drives all logicWhen you need highly testable presentation logic
MVVM (Model-View-ViewModel)Two-way data binding between View and ViewModelRich client apps (WPF, SwiftUI, Vue.js, Angular)
ObserverUpdates UI when model state changesReactive UIs, real-time dashboards
CommandEncapsulates user actions (button clicks, menu items)When actions need undo/redo, queueing, or logging
MediatorCoordinates complex form/dialog interactionsMulti-panel UIs where components interact heavily
StrategySwitches rendering/formatting logicWhen display format varies (list/grid/card views)
StateManages UI state transitionsMulti-step wizards, form validation states

Business Logic Layer

Contains domain rules, workflows, and application-specific logic.

PatternRole in This LayerWhen to Choose
StrategyEncapsulates interchangeable business algorithmsPricing rules, discount calculations, tax computation
StateModels domain object lifecyclesOrder processing (draft -> submitted -> approved -> shipped)
Chain of ResponsibilityRoutes business decisions through handler pipelineApproval workflows, validation chains, rule engines
SpecificationDefines composable business rulesComplex filtering criteria, eligibility checks
Template MethodFixed workflow with customizable stepsReport generation, ETL processes, onboarding flows
CommandEncapsulates domain operationsTask scheduling, audit logging, transactional operations
VisitorApplies operations across a domain hierarchyTax calculation across product categories, export formats
ObserverDomain event notificationSaga/process manager patterns, cross-aggregate communication
InterpreterEvaluates domain-specific languagesRule engines, formula evaluators, query builders

Data Access Layer

Manages persistence, queries, and data transformation.

PatternRole in This LayerWhen to Choose
RepositoryAbstracts data access as a collection-like interfaceWhen domain model should be persistence-ignorant
Data MapperTranslates between domain objects and database schemaWhen domain model differs significantly from DB schema
Active RecordObjects know how to persist themselvesSimple CRUD apps with 1:1 model-table mapping (Rails, Laravel)
Unit of WorkTracks changes and commits them atomicallyWhen multiple entities change in a single business operation
DAO (Data Access Object)Encapsulates all access to a data sourceSimpler alternative to Repository when DDD is not used
Identity MapEnsures each DB row maps to exactly one in-memory objectPrevents duplicate objects and inconsistent state
Query ObjectEncapsulates a database query as an objectDynamic search/filter scenarios
Lazy LoadDefers loading of related data until accessedPerformance optimization for large object graphs

Infrastructure Layer

Handles external systems, frameworks, and technical services.

PatternRole in This LayerWhen to Choose
AdapterWraps external service APIs to match internal interfacesIntegrating third-party APIs, legacy system migration
FacadeSimplifies complex infrastructure subsystemsWrapping email/SMS/push notification services behind one API
ProxyControls access to infrastructure resourcesCaching proxies, circuit breakers, rate limiters
FactoryCreates infrastructure components based on configurationDatabase driver selection, cloud provider abstraction
BuilderConstructs complex configuration objectsConnection strings, HTTP clients, message producers
SingletonManages shared infrastructure resourcesConnection pools, configuration managers
GatewayEncapsulates access to an external systemREST/SOAP/gRPC service wrappers

Cross-Cutting Concerns

Aspects that span multiple layers: logging, security, caching, monitoring.

PatternRoleWhen to Choose
Interceptor / AOPInjects behavior before/after method callsLogging, authentication, performance monitoring
DecoratorWraps objects to add cross-cutting behaviorAdding caching, retry logic, circuit breaking to service calls
Observer / Pub-SubDecouples event producers from consumersAudit logging, analytics events, domain events
ProxyIntercepts calls for security/caching/loggingTransaction management, access control
Chain of ResponsibilityPipelines for cross-cutting processingMiddleware stacks (ASP.NET, Express.js)
StrategySwappable implementations for cross-cutting concernsPluggable logging backends, cache providers

Pattern Complexity and Learning Path

Not all patterns are equal in difficulty or frequency of use. This section provides difficulty rankings and a recommended learning order to build understanding incrementally.

Difficulty Rankings

Beginner Level (Learn First)

Patterns with intuitive concepts, straightforward implementation, and high practical frequency.

PatternDifficultyFrequency of UseWhy Learn First
Factory Method2/5Very HighFoundation for understanding all creational patterns
Strategy2/5Very HighTeaches composition over inheritance; used everywhere
Observer2/5Very HighCore of event-driven programming and UI frameworks
Singleton1/5HighEasy to understand; teaches global state trade-offs
Iterator1/5Very HighBuilt into every modern language; reinforces abstraction
Facade1/5HighSimple concept; immediate practical benefit
Adapter2/5HighDirectly maps to real integration problems
Template Method2/5HighNatural use of inheritance; common in frameworks

Intermediate Level (Learn Second)

Patterns that require understanding of composition, delegation, and lifecycle management.

PatternDifficultyFrequency of UseWhy Learn Second
Decorator3/5HighIntroduces dynamic composition; builds on Strategy concepts
Command3/5HighEssential for undo systems and task queues
Composite3/5Medium-HighRecursive structures; builds on Iterator
Builder2/5HighPractical and common; pairs naturally with Factory
State3/5Medium-HighReplaces complex conditionals; similar to Strategy
Proxy3/5Medium-HighSimilar structure to Decorator but different intent
Chain of Responsibility3/5MediumBuilds on Decorator concepts for pipelines
Mediator3/5MediumCentralizes Observer-like communication
Abstract Factory3/5MediumExtension of Factory Method to families
Memento3/5MediumPairs with Command for undo systems

Advanced Level (Learn Third)

Patterns with complex mechanics, niche use cases, or requiring deep OOP understanding.

PatternDifficultyFrequency of UseWhy Learn Last
Visitor4/5Low-MediumDouble dispatch is non-intuitive; niche use cases
Bridge4/5Low-MediumAbstract concept; often confused with Adapter
Flyweight4/5LowMemory optimization; only needed at scale
Prototype3/5Low-MediumCloning semantics (deep vs shallow) are tricky
Interpreter5/5LowRequires understanding of grammars and parsing
Null Object2/5MediumEasy concept but requires design maturity to apply well

Based on Joshua Kerievsky’s “A Learning Guide to Design Patterns” (Industrial Logic), this 23-session curriculum builds each pattern on the foundations of the ones before it:

PhaseSessionPatternRationale
Foundation1Factory MethodGateway to creational patterns
2StrategyMost frequently needed behavioral pattern
3DecoratorIntroduces dynamic composition elegantly
4CompositeAppears in many real systems
5IteratorReinforces Composite; universally used
6Template MethodNatural inheritance pattern; builds on prior
Core Creational7Abstract FactoryExtends Factory Method concepts
8BuilderStep-by-step construction
9SingletonSimple but important to understand trade-offs
Structural10ProxyAccess control and lazy loading
11AdapterInterface translation
12BridgeAbstraction/implementation separation
Core Behavioral13MediatorCentralized communication
14ObserverEvent notification (builds on Mediator)
15Chain of ResponsibilityHandler pipelines
16MementoState snapshots
17CommandAction encapsulation (pairs with Memento)
Advanced18PrototypeCloning mechanics
19StateState machines
20VisitorDouble dispatch
21FlyweightMemory optimization
22InterpreterLanguage processing
23FacadeSystem simplification (capstone)

Decision Flowcharts

When you face a design problem, start here. These textual decision trees guide you from problem description to pattern selection.

Master Decision Tree

START: What type of problem are you solving?
|
+---> CREATING OBJECTS? -----------------------------> [Creational Branch]
|
+---> COMPOSING/STRUCTURING objects or classes? ------> [Structural Branch]
|
+---> MANAGING BEHAVIOR, communication, or algorithms? -> [Behavioral Branch]
|
+---> CROSS-CUTTING or ARCHITECTURAL concern? --------> [Architectural Branch]

Creational Branch

Is your problem about CREATING OBJECTS?
|
+---> Do you need exactly ONE instance globally?
|     +---> Yes, and it must be thread-safe -----------> Singleton
|     +---> Consider: Would Dependency Injection work? -> DI Container (preferred)
|
+---> Do you need to hide/vary the creation logic?
|     +---> Creating ONE product type?
|     |     +---> Decision based on simple input? -----> Simple Factory
|     |     +---> Subclasses decide what to create? ---> Factory Method
|     +---> Creating FAMILIES of related products? ----> Abstract Factory
|
+---> Do you need step-by-step construction?
|     +---> Object has many optional parameters? ------> Builder
|     +---> Same construction, different output? ------> Builder
|     +---> Need fluent/readable API? -----------------> Builder + Fluent Interface
|
+---> Do you need copies of existing objects?
|     +---> Construction is expensive? -----------------> Prototype
|     +---> Need to avoid subclass proliferation? -----> Prototype
|
+---> Do you need to recycle expensive objects?
      +---> DB connections, threads, sockets? ----------> Object Pool

Structural Branch

Is your problem about STRUCTURING objects?
|
+---> Do you need to make two things work together?
|     +---> Converting one interface to another? ------> Adapter
|     |     +---> Wrapping a class? -------------------> Class Adapter (inheritance)
|     |     +---> Wrapping an object? -----------------> Object Adapter (composition)
|     +---> Need abstraction AND implementation to vary independently?
|           +---> Preventing class explosion? ----------> Bridge
|
+---> Do you need to build something from parts?
|     +---> Tree/hierarchy where parts = wholes? ------> Composite
|     +---> Need uniform treatment of leaves & nodes? -> Composite
|
+---> Do you need to add behavior?
|     +---> At runtime, dynamically? ------------------> Decorator
|     +---> Stack multiple behaviors? -----------------> Decorator
|     +---> Inheritance would cause class explosion? --> Decorator
|
+---> Do you need to simplify something complex?
|     +---> Provide a simple interface to subsystem? --> Facade
|
+---> Do you need to optimize memory?
|     +---> Many objects sharing common state? ---------> Flyweight
|     +---> Intrinsic vs extrinsic state separation? --> Flyweight
|
+---> Do you need to control access?
      +---> Lazy initialization? -----------------------> Virtual Proxy
      +---> Access control / security? -----------------> Protection Proxy
      +---> Remote object access? ----------------------> Remote Proxy
      +---> Caching? ----------------------------------> Caching Proxy

Behavioral Branch

Is your problem about OBJECT BEHAVIOR?
|
+---> ALGORITHM PROBLEMS
|     +---> Need to swap algorithms at runtime? -------> Strategy
|     +---> Fixed skeleton, variable steps? -----------> Template Method
|     |     (Difference: Strategy uses composition, Template Method uses inheritance)
|     +---> Need to interpret a language/grammar? -----> Interpreter
|
+---> COMMUNICATION PROBLEMS
|     +---> One-to-many notification? -----------------> Observer
|     |     +---> Need stronger decoupling? -----------> Publish-Subscribe / Event Bus
|     +---> Many-to-many communication too complex? ---> Mediator
|     +---> Sender shouldn't know the receiver? -------> Chain of Responsibility
|
+---> STATE MANAGEMENT PROBLEMS
|     +---> Behavior changes with internal state? -----> State
|     |     (Difference from Strategy: State transitions happen internally)
|     +---> Need to save/restore state? ---------------> Memento
|     +---> Need undo/redo? ---------------------------> Command + Memento
|
+---> ACTION ENCAPSULATION
|     +---> Need to parameterize objects with actions? -> Command
|     +---> Need to queue/schedule actions? -----------> Command
|     +---> Need to log/audit actions? ----------------> Command
|     +---> Need macro/composite actions? -------------> Command + Composite
|
+---> TRAVERSAL PROBLEMS
|     +---> Need to traverse a collection uniformly? --> Iterator
|     +---> Need to traverse a tree structure? --------> Iterator + Composite
|     +---> Need to perform operations during traversal? -> Visitor
|
+---> EXTENSION PROBLEMS
|     +---> Add operations without modifying classes? --> Visitor
|     +---> Handle null without null checks? -----------> Null Object
|
+---> RESPONSIBILITY DISTRIBUTION
      +---> Request processed by one of many handlers? -> Chain of Responsibility
      +---> Request processed by ALL handlers in sequence? -> Chain of Responsibility
                                                              (pipeline variant)

Architectural Branch

Is your problem ARCHITECTURAL or CROSS-CUTTING?
|
+---> PRESENTATION ARCHITECTURE
|     +---> Server-rendered web app? ------------------> MVC
|     +---> Need highly testable presentation? --------> MVP
|     +---> Rich client with data binding? ------------> MVVM
|
+---> DATA ACCESS ARCHITECTURE
|     +---> Domain-driven design? ---------------------> Repository + Data Mapper
|     +---> Simple CRUD app? --------------------------> Active Record
|     +---> Multiple entity changes per operation? ----> Unit of Work
|     +---> Need to abstract all data access? ---------> DAO
|
+---> SCALABILITY / SEPARATION
|     +---> Read and write models differ? -------------> CQRS
|     +---> Need full audit trail / event replay? -----> Event Sourcing
|     +---> Need to decouple services? ----------------> Publish-Subscribe / Message Queue
|
+---> CROSS-CUTTING CONCERNS
      +---> Logging, auth, monitoring? ----------------> Interceptor / AOP
      +---> Need to add behavior to service calls? ----> Decorator or Proxy
      +---> Middleware pipeline? -----------------------> Chain of Responsibility

Quick-Reference: Commonly Confused Patterns

Strategy vs. State vs. Command:

STRATEGY: "I have multiple ways to DO something"
  -> Client chooses the algorithm externally
  -> Example: sorting (quicksort vs mergesort)

STATE: "I behave differently DEPENDING ON my condition"
  -> Object transitions between states internally
  -> Example: TCP connection (listening/established/closed)

COMMAND: "I need to REPRESENT an action as an object"
  -> Decouples invoker from executor
  -> Example: menu item that triggers any action

Adapter vs. Bridge vs. Facade vs. Proxy vs. Decorator:

ADAPTER:   Changes the interface    (make X look like Y)
BRIDGE:    Separates abstraction    (X and Y evolve apart)
FACADE:    Simplifies the interface (hide complexity of X, Y, Z)
PROXY:     Controls access          (guard/cache/lazy-load X)
DECORATOR: Adds behavior            (enhance X without change)

When NOT to Use a Pattern

This is as important as knowing when to use one.

PatternDo NOT Use When…
SingletonYou are using it as a global variable; when you need testability; when multiple instances might be needed later
ObserverThere are too many observers causing performance issues; update order matters (use Mediator instead)
FactoryThere is only one concrete type and no foreseeable variation
DecoratorThe order of decoration matters and is hard to control; a few subclasses would be simpler
StrategyThere are only two variants and no expected growth (simple if/else suffices)
VisitorThe class hierarchy changes frequently (adding classes requires modifying all visitors)
Chain of ResponsibilityYou always know exactly which handler should process the request
MediatorIt becomes a “God object” that knows too much about every colleague
CommandThe action is trivial and will never need undo, queuing, or logging

Anti-Patterns: The Patterns of Failure

If design patterns are proven solutions to recurring problems, anti-patterns are proven ways to create recurring problems. Understanding them is essential because they often result from the misapplication of design patterns.

God Object

Definition: A single class or object that handles far too many responsibilities, creating a broad, unfocused interface.

Symptoms: One class that knows about user data, transactions, UI logic, database access, and business rules simultaneously. Everywhere in the codebase depends on it.

Consequences: Violates Single Responsibility Principle; forces implementing classes to include unnecessary properties; becomes a bottleneck for all changes; nearly impossible to test in isolation.

Pattern Misuse Connection: Often results from failing to apply proper decomposition patterns (Strategy, Observer, Facade) or misunderstanding the Singleton pattern as a dumping ground.

Spaghetti Code

Definition: Code with tangled control flow, lacking structure, organization, and modularity.

Symptoms: Random file placement, arbitrary jumps, long functions, absence of modularity, cross-dependencies, difficult-to-follow execution paths.

Consequences: Makes maintenance nearly impossible; prevents accurate project estimation; “you will constantly break things, not understand the scope of your changes.”

Pattern Misuse Connection: Represents the complete absence of intentional architectural patterns; the antithesis of structured pattern-based design.

Golden Hammer

Definition: Over-reliance on a single familiar tool, technology, or pattern for all problems regardless of appropriateness.

Symptoms: Applying the same solution pattern everywhere despite poor fit; resisting new tools or methodologies; “when all you have is a hammer, everything looks like a nail.”

Consequences: Performance degradation, increased development time, language-specific problems when approaches do not transfer across contexts.

Pattern Misuse Connection: Directly relates to dogmatic pattern application --- using Singleton everywhere, forcing Observer into every communication channel, or applying Factory when simple constructors suffice.

Premature Optimization

Definition: Optimizing code for performance before knowing whether the optimization is actually needed.

Symptoms: Complex caching layers without profiling data; hand-rolled data structures replacing standard library ones; micro-optimizations that obscure business logic.

Consequences: Increased complexity, reduced readability, decreased maintainability, wasted development time.

Pattern Misuse Connection: Applying Flyweight, Object Pool, or Proxy patterns for performance reasons without evidence of a performance problem. As Donald Knuth stated: “premature optimization is the root of all evil.”

Copy-Paste Programming

Definition: Copying and pasting source code instead of creating reusable abstractions.

Symptoms: Nearly identical code blocks scattered across the codebase; bug fixes applied in one location but not duplicated locations; increasing divergence between copies over time.

Consequences: Multiplied bug surface, inconsistent behavior, exponential maintenance cost.

Pattern Misuse Connection: Indicates failure to recognize opportunities for Template Method, Strategy, or simple extraction into shared components; the opposite of the DRY principle that patterns formalize.

Lava Flow

Definition: Old, fragile code remaining in the system because no one fully understands or dares to remove it --- like hardened lava that was once fluid but has cooled into a rigid, immovable structure.

Symptoms: Code written under sub-optimal conditions deployed to production and expanded upon while still developmental; mysterious modules that “might break something” if removed.

Consequences: Accumulating dead weight, increasing build times, false dependencies, fear-driven development.

Pattern Misuse Connection: Often contains abandoned pattern implementations from previous architectural visions; patterns half-implemented during prototyping that solidified into production without completion.

Boat Anchor

Definition: Retaining unnecessary code “in case it is needed later” despite not meeting current specifications.

Symptoms: Obsolete code mixed with functional code; commented-out blocks; unused interfaces and abstract classes kept “just in case.”

Consequences: Extended build times, maintenance confusion, potential accidental activation in production, accumulated technical debt.

Pattern Misuse Connection: Creates false implementation patterns that obfuscate actual architectural intent; over-engineered pattern infrastructure built for requirements that never materialized.

Dead Code

Definition: Code whose functionality is no longer required but remains in the codebase, often with unclear context about its purpose.

Symptoms: Functions called everywhere but serving no purpose; uncertainty about necessity; common in proof-of-concept code that reaches production.

Consequences: Increased debugging difficulty, knowledge gaps about actual system requirements, management resistance to cleanup.

Pattern Misuse Connection: Represents patterns implemented for obsolete requirements, creating misleading architectural assumptions; developers read the dead code and assume the patterns it implements are still relevant.

How Anti-Patterns Relate to Pattern Misuse

RelationshipDescription
Over-applicationGolden Hammer: forcing patterns where they add no value; every problem becomes a Factory or Strategy
Under-applicationSpaghetti Code: failing to recognize where patterns would bring structure and clarity
Premature applicationBoat Anchor / Premature Optimization: implementing complex pattern infrastructure before requirements justify it
Abandoned applicationLava Flow / Dead Code: half-implemented patterns from prior visions that calcify in the codebase
Misunderstood applicationGod Object: using Singleton as an excuse for a global dumping ground instead of a controlled single instance
Duplicated logicCopy-Paste Programming: failing to see that repeated code is a signal to extract a Template Method or Strategy

Essential Books

“Design Patterns: Elements of Reusable Object-Oriented Software” (GoF, 1994)

  • Authors: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  • The canonical reference. Cataloged 23 foundational patterns. Established the formal template for documenting patterns. Popularized “Program to an interface, not an implementation” and “Favor object composition over class inheritance.” Examples in C++ and Smalltalk.

“Head First Design Patterns” (Freeman & Robson)

  • Publisher: O’Reilly Media (1st ed. 2004, 2nd ed. 2021)
  • The most accessible entry point for learning design patterns. Visually rich, brain-friendly format with puzzles, humor, and conversational tone. Builds understanding incrementally through real-world scenarios.

“Patterns of Enterprise Application Architecture” (Martin Fowler, 2002)

  • Defined the vocabulary for enterprise application architecture. Active Record, Data Mapper, Unit of Work, and Repository remain foundational in modern frameworks (Rails, Django, Entity Framework, Hibernate). Covers domain logic, data source, web presentation, distribution, offline concurrency, and session state patterns.

“Domain-Driven Design” (Eric Evans, 2003)

  • Strategic patterns (Bounded Context, Context Mapping, Ubiquitous Language, Anti-Corruption Layer) and tactical patterns (Entity, Value Object, Aggregate, Repository, Factory, Domain Service, Domain Event, Specification). DDD patterns operate at a higher abstraction level than GoF --- they address the domain model and system boundaries. GoF patterns are used within DDD implementations.

“Clean Architecture” (Robert C. Martin, 2017)

  • The Dependency Rule, concentric architecture layers, architectural boundaries, and the Humble Object pattern. Builds on Hexagonal Architecture (Ports and Adapters) and Onion Architecture. Integrates SOLID principles as the foundation for component-level and architectural-level decisions.

“Refactoring to Patterns” (Joshua Kerievsky, 2004)

  • Bridges refactoring and patterns with 27 pattern-directed refactorings. Demonstrates that patterns need not be designed up-front but can be evolved into as a system grows. 12 design smells that indicate the need for pattern-based refactoring.

“Implementation Patterns” (Kent Beck, 2007)

  • 77 patterns for everyday programming tasks. Focuses on the micro-level: how to write individual classes, methods, and variables that clearly communicate intent. Complements GoF patterns by addressing the code-level decisions that GoF patterns are built upon.

“Pattern-Oriented Software Architecture” (POSA) Vol 1-5

VolumeFocusYear
Vol 1Architectural patterns (Layers, Pipes and Filters, Blackboard), design patterns, idioms1996
Vol 217 patterns for concurrent and networked systems2000
Vol 3Complete resource lifecycle patterns2004
Vol 4Pattern language for distributed computing2007
Vol 5Meta-level: what patterns are and are not, developing pattern languages2007

Top Online Learning Resources

#ResourceURLWhy Valuable
1Refactoring.Guruhttps://refactoring.guru/design-patternsVisual-heavy, multi-language (9 languages), integrates patterns with refactoring and code smells
2SourceMakinghttps://sourcemaking.com/design_patternsProblem-solution format, includes criticism section, covers 26 patterns including non-GoF
3Patterns.devhttps://www.patterns.dev/Free, modern web-focused patterns (React, vanilla JS), rendering and performance patterns
4GeeksforGeeks Design Patterns Cheat Sheethttps://www.geeksforgeeks.org/system-design/design-patterns-cheat-sheet-when-to-use-which-design-pattern/Complete decision guide: when to use which pattern, organized by problem type
5Hackr.io Design Patternshttps://hackr.io/tutorials/learn-software-design-patternsCommunity-voted rankings of courses and tutorials across all languages
6DZone Refcardz: Design Patternshttps://dzone.com/refcardz/design-patternsConcise reference card covering all 23 GoF patterns with diagrams and examples
7ByteByteGo Design Patterns Cheat Sheethttps://blog.bytebytego.com/p/ep17-design-patterns-cheat-sheetSystem design perspective, visual cheat sheets
8Game Programming Patternshttp://gameprogrammingpatterns.com/design-patterns-revisited.htmlFree online book, revisits GoF patterns through game development lens
9F# for Fun and Profit: FP Patternshttps://fsharpforfunandprofit.com/fppatterns/Functional programming design patterns, bridges OOP and FP paradigms
10Java Code Geeks Cheatsheethttps://www.javacodegeeks.com/design-patterns-cheatsheet.htmlDownloadable PDF, Java-focused, concise quick-reference

Video Courses

#TitlePlatformWhy Valuable
1Design Patterns LibraryPluralsightComprehensive library covering all major patterns with descriptions, examples, and techniques
2Design Patterns in JavaUdemyImplements classic patterns using modern Java 8+ features (lambdas, streams)
3Design Patterns (University of Alberta)CourseraAcademic rigor with hands-on projects; covers OO principles, common patterns, and architecture
4Design Patterns OverviewPluralsightHow patterns are discovered, defined, and applied; ideal starting point
5Practical Design Patterns in JavaScriptPluralsightPatterns specifically for JavaScript; web development context

Quick-Reference Cheat Sheets

ResourceFormatURL
DZone RefcardzPDFhttps://dzone.com/refcardz/design-patterns
RIT SE Reference CardPDFhttps://www.se.rit.edu/~swen-383/resources/RefCardz/designpatterns.pdf
McdonaldLand CardPDFhttps://www.mcdonaldland.info/files/designpatterns/designpatternscard.pdf
GitHub GoF Cheat SheetGisthttps://gist.github.com/kevinCefalu/c160afd09b2802c01e3dfc02d09ed677

Community Debates Worth Reading

“Are Design Patterns Still Relevant?”

The consensus across multiple sources: design patterns remain relevant as conceptual tools and shared vocabulary, but their implementation has evolved. Modern developers should understand patterns but apply them judiciously, using language-native features when they provide the same benefit with less ceremony.

Key perspectives:

  • Yes, more than ever: System complexity explosion and cross-domain integration requirements make patterns a competitive necessity.
  • Yes, but evolved: The 23 GoF patterns remain relevant as vocabulary; patterns must be adapted to modern language features.
  • Partially obsolete: Many classic patterns are workarounds for language limitations that modern languages have solved.
  • Context-dependent: Relevance varies by language. Each pattern must be evaluated against each language’s capabilities.
  • Valuable as vocabulary: Even critics acknowledge patterns provide a shared language for communicating design decisions.

“SOLID Principles vs. Design Patterns”

SOLID provides the “why” (guidelines and constraints). Design patterns provide the “how” (concrete solutions and templates). They are complementary:

SOLID PrinciplePatterns That Embody It
S - Single ResponsibilityStrategy, Observer
O - Open/ClosedDecorator, Strategy
L - Liskov SubstitutionFactory Method, Template Method
I - Interface SegregationAdapter, Facade
D - Dependency InversionAbstract Factory, Observer

Series Conclusion: The Pattern Mindset

Over ten posts, we have traveled from the foundations of software design patterns to the frameworks for choosing them. Here is the arc of what we have covered:

Posts 1-2 established what design patterns are and why they matter --- a shared vocabulary born from the Gang of Four’s 1994 catalog, rooted in Christopher Alexander’s architectural pattern language. We grounded patterns in the SOLID principles that give them their theoretical backbone.

Posts 3-5 dove into the three categories of GoF patterns: creational patterns (how objects are born), structural patterns (how objects are composed), and behavioral patterns (how objects communicate). Each pattern was examined not just for its mechanics but for its trade-offs and real applicability.

Posts 6-7 expanded beyond the GoF canon into architectural patterns (MVC, microservices, event-driven architecture) and the principles that guide pattern application --- composition over inheritance, dependency inversion, separation of concerns, and the principle of least knowledge.

Post 8 revealed that patterns are not universal constants --- they are shaped by language features and programming paradigms. What requires three classes in Java is a one-liner in Python. Rust’s type system enables patterns (Typestate, Newtype) that cannot exist elsewhere. Functional programming replaces entire pattern categories with function composition and algebraic data types.

Post 9 showed patterns in the wild --- running the Linux kernel, powering Netflix’s resilience infrastructure, organizing Uber’s 2,200 microservices, and evolving through jQuery’s rise and fall, React’s HOC-to-Hooks transition, and the microservices revival of 1990s distributed systems patterns.

This post (Post 10) provides the decision framework --- the practical tools for choosing the right pattern for the right problem at the right time.

The Five Principles of Pattern Application

If this entire series distills into five principles, they are these:

  1. Start with the problem, not the pattern. The decision flowcharts in this post exist because good pattern selection begins with a clear problem statement. “I need to notify multiple objects of state changes” leads to Observer. “This code looks like it could use Observer” leads to over-engineering.

  2. Patterns are a vocabulary, not a checklist. Their primary value is enabling developers to communicate design intent efficiently. “This is a Strategy” tells a teammate everything they need to know about the structure, intent, and trade-offs of your code.

  3. Language and paradigm matter. A pattern that is essential in Java may be invisible in Python and irrelevant in Haskell. Always evaluate whether a language feature provides the same benefit with less ceremony.

  4. Patterns evolve. jQuery’s Facade, React’s HOCs, and JavaScript callbacks were all valuable patterns that were superseded. The Circuit Breaker pattern did not exist in the GoF book but is essential in modern distributed systems. Stay current.

  5. The best pattern is sometimes no pattern. The anti-patterns section of this post exists because pattern misuse --- over-application, premature application, and misunderstood application --- creates worse code than no patterns at all. Every pattern must earn its place in your codebase by solving a real problem that justifies its complexity.

Design patterns are tools for thinking. They encode decades of collective experience into reusable solutions. But like any tool, they require judgment in application. The developer who knows 23 patterns and applies them mechanically will produce worse software than the developer who knows 5 patterns and applies them with wisdom.

The goal was never to memorize a catalog. It was to develop the pattern mindset --- the ability to recognize recurring problems, recall proven solutions, evaluate trade-offs, and make deliberate design choices. If this series has helped you develop that mindset, it has done its job.


Sources

Decision Framework

Anti-Patterns

Community Resources and Debates

Books

Sources & References

  1. Refactoring Guru --- Design Patterns Catalog (accessed 2026-03-11)
  2. SourceMaking --- Design Patterns (accessed 2026-03-11)
  3. GeeksforGeeks --- Design Patterns Cheat Sheet (accessed 2026-03-11)
  4. JavaTechOnline --- When to Use Which Design Pattern: 23 GoF Patterns (accessed 2026-03-11)
  5. Industrial Logic --- A Learning Guide to Design Patterns (Joshua Kerievsky) (accessed 2026-03-11)
  6. Design Gurus --- Classification of Design Patterns (accessed 2026-03-11)
  7. AlgoCademy --- Understanding Design Patterns (accessed 2026-03-11)
  8. Zimmer (1995) --- Relationships Between Design Patterns (accessed 2026-03-11)
  9. Microservices.io --- Better Decision Making with Pattern-Style Thinking (accessed 2026-03-11)
  10. Microsoft Learn --- Common Web Application Architectures (accessed 2026-03-11)
  11. O'Reilly --- Software Architecture Patterns: Layered Architecture (accessed 2026-03-11)
  12. LinkedIn --- How Design Patterns Solve Cross-Cutting Concerns (accessed 2026-03-11)
  13. Beezwax --- The Repository and Unit of Work Design Patterns (accessed 2026-03-11)
  14. Gamma, Helm, Johnson, Vlissides --- Design Patterns: Elements of Reusable Object-Oriented Software (GoF Book) (accessed 2026-03-11)
  15. freeCodeCamp --- Antipatterns to Avoid in Code (accessed 2026-03-11)
  16. Wikipedia --- List of Software Anti-Patterns (accessed 2026-03-11)
  17. Baeldung --- Anti-Patterns (accessed 2026-03-11)
  18. Lucidchart --- What Are Software Anti-Patterns (accessed 2026-03-11)
  19. Minware --- Premature Optimization (accessed 2026-03-11)
  20. Minware --- Lava Flow (accessed 2026-03-11)
  21. Wikipedia --- Lava Flow (programming) (accessed 2026-03-11)
  22. Patterns.dev (accessed 2026-03-11)
  23. Game Programming Patterns --- Robert Nystrom (accessed 2026-03-11)
  24. F# for Fun and Profit: FP Patterns --- Scott Wlaschin (accessed 2026-03-11)
  25. Are Design Patterns Still Relevant in 2025? --- Freddy Dordoni (accessed 2026-03-11)
  26. Are Design Patterns Still Relevant --- Mario Cervera (accessed 2026-03-11)
  27. Design Patterns in Functional Programming --- DEV Community (accessed 2026-03-11)
  28. FP Alternative to the Strategy Pattern --- Expedia Group (accessed 2026-03-11)
  29. SOLID vs Design Patterns --- Tolga Yildiz (accessed 2026-03-11)
  30. Design Principles vs Design Patterns --- TutorialsTeacher (accessed 2026-03-11)
  31. Martin Fowler --- Refactoring to Patterns (accessed 2026-03-11)
  32. POSA Series (accessed 2026-03-11)
  33. Pluralsight Design Patterns Library (accessed 2026-03-11)
  34. Coursera Design Patterns (University of Alberta) (accessed 2026-03-11)
  35. C# Decision Tree for Design Patterns --- Vivek Baliyan (accessed 2026-03-11)
  36. ByteByteGo Design Patterns Cheat Sheet (accessed 2026-03-11)

Sources compiled from official documentation, academic papers, and community resources gathered during research for the Software Design Patterns series.