Guide
Choosing a Pattern
A decision guide that maps concrete code symptoms to the pattern that fixes them, and tells you when plain code beats any pattern.
- Also known as
- Pattern selection, Pattern catalog
Start from the symptom, not the pattern
Pick a pattern only after you can name the concrete problem in the current code that it removes. "This would be more flexible" is not a problem. "Adding a fourth export format means editing the same switch in five files" is.
Work in this order:
- Name the pain in one sentence, with a file and function.
- Check the "Prefer no pattern when" list below. Most pains have a smaller fix.
- Find the symptom in the table. Read the candidate pattern's
## Do not use it whensection before anything else. - If two patterns fit, compare their
## Relationssections. They state the difference directly.
Symptom to pattern
| Symptom in the code | Candidate | Check first |
|---|---|---|
new ConcreteThing() scattered in code that should not know which concrete type it gets | Factory Method | A plain factory function is often enough |
| Several related objects must be created as a matched set (all AWS or all GCP clients) | Abstract Factory | Only worth it with two or more real families |
| Constructor with many optional parameters, or objects built in several steps | Builder | Named or object parameters may do it |
| Creating an object is expensive and you need many near-identical copies | Prototype | structuredClone or a copy constructor |
| Exactly one instance must exist and be reachable process-wide | Singleton | Usually pass the instance in instead |
| A third-party or legacy class has the wrong interface for your code | Adapter | A wrapper function for one call |
| Two independent dimensions of variation multiply into many subclasses | Bridge | Only if both dimensions really vary |
| Tree of items where leaves and groups must be treated the same | Composite | A recursive function over plain data |
| You need to add behavior (logging, caching, retries) around an object without changing it | Decorator | Higher-order function or middleware |
| Callers wire together many subsystem objects to do one common task | Facade | A single module-level function |
| Memory blows up from millions of objects that share most of their data | Flyweight | Measure first |
| Access to an object needs lazy loading, access control or remote calls | Proxy | Language-level Proxy or a lazy getter |
| A request passes through a sequence of handlers, any of which may handle or reject it | Chain of Responsibility | An array of functions in a loop |
| Actions must be queued, logged, retried or undone | Command | A closure plus a record of inputs |
| Callers depend on a collection's internal layout to traverse it | Iterator | Built-in iterators and generators |
| Many components call each other directly, forming a tangle | Mediator | An event bus you already have |
| You need snapshots for undo without exposing internal fields | Memento | Immutable state makes this free |
| Other objects must react when something changes | Observer | Built-in events, signals or streams |
Behavior depends on an object's mode, with switch (this.status) in many methods | State | A transition table if behavior is small |
A switch picks between algorithms that compute the same result in different ways | Strategy | Pass a function |
| Subclasses copy the same workflow and differ in a few steps | Template Method | Higher-order function with hooks |
| You keep adding operations over a stable set of node types (AST, document tree) | Visitor | Pattern matching over a union type |
Prefer no pattern when
- There are only one or two variants. An
ifis honest. Wait for the third case. See Speculative Generality. - The language already has the feature. First-class functions replace most single-method Strategy, Command and Template Method uses. Generators replace Iterator. Discriminated unions with exhaustive
switchreplace many Visitor uses. Module scope replaces Singleton. - A refactoring technique removes the smell. Extract Method, Introduce Parameter Object and Decompose Conditional fix more code than any pattern.
- The code is not going to change. Patterns pay off on the next change. Stable code gets no return.
- The team or codebase does not use the pattern elsewhere. One lone Abstract Factory in a codebase of plain functions is a reading tax for every future contributor.
- You cannot write a test that the pattern makes easier. If the test is the same before and after, the pattern bought nothing measurable.
- The framework already owns that concern. Dependency injection containers, ORMs and UI frameworks implement Factory, Proxy and Observer for you. Use theirs.
Selection flowchart
The first chart decides whether you need a pattern at all and which family to look in. Families are explained in Classification of patterns.
Diagram source (Mermaid)
flowchart TD
A{"Can you name a concrete pain in current code?"}
A -->|No| N["Use no pattern"]
A -->|Yes| B{"Would a function, map or refactoring fix it?"}
B -->|Yes| N
B -->|No| C{"Is the pain about creating objects?"}
C -->|Yes| CR["Creational patterns"]
C -->|No| D{"Is it about fitting or wrapping objects?"}
D -->|Yes| ST["Structural patterns"]
D -->|No| BH["Behavioral patterns"]Behavioral is the largest family and the most common destination. The second chart covers its frequent cases.
Diagram source (Mermaid)
flowchart TD
A{"Does a switch pick an algorithm per call?"}
A -->|Yes| S["Strategy, or pass a function"]
A -->|No| B{"Does behavior depend on a mode that changes over time?"}
B -->|Yes| ST["State"]
B -->|No| C{"Must others react when something changes?"}
C -->|Yes| O["Observer"]
C -->|No| T["Use the symptom table"]Telling look-alikes apart
Several patterns share a class diagram. Decide by intent:
- Strategy vs State: strategies are chosen from outside and ignore each other; states switch themselves.
- Decorator vs Proxy: a decorator adds behavior and stacks; a proxy controls access and usually manages the real object's lifecycle.
- Adapter vs Facade: an adapter makes one existing interface fit another; a facade invents a simpler interface over many objects.
- Factory Method vs Abstract Factory: one product vs a family of products. See Factory comparison.
- Command vs Strategy: a command represents a request to run later; a strategy represents a way to compute now.
Agent checklist
- You can state the pain in one sentence that names real code.
- You checked the "Prefer no pattern when" list and none of it applies.
- You read the chosen pattern's
## Do not use it whensection and none of it applies. - At least three variants exist, or a third is concretely planned.
- A test exists, or will exist, that is easier to write after the change.
- Existing tests still pass after you introduce the pattern.
See also
- How Agents Should Use This ReferenceRules for AI coding agents using this reference: diagnose a named smell, read the exclusions first, apply one technique at a time and verify with tests.
- What Is a Design PatternA design pattern is a named, reusable solution shape for a recurring design problem. Learn what one contains and how it differs from an algorithm or library.
- Criticism of PatternsThe main arguments against design patterns, including missing language features, misuse, and over-engineering, and how to apply patterns without those failures.
- Classification of PatternsHow the 22 classic design patterns are grouped by purpose into creational, structural and behavioral, plus other useful ways to slice them.
- StrategyPut each interchangeable algorithm behind one interface so the caller picks a behavior at runtime without branching on it.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.