---
name: aipatterns-pattern-selection
description: Decide whether the code needs a design pattern, and which one. Use before you introduce any GoF pattern such as factory, builder, singleton, adapter, decorator, strategy, observer or visitor. Use when a user asks "which pattern should I use". Use when you review code that adds abstraction layers. Use when you want an interface "for flexibility". Default to no pattern unless a concrete, current pain justifies one.
license: MIT
metadata:
  source: https://aipatterns.dev/guides/choosing-a-pattern
  version: "1.0.0"
---

# Pattern selection

Your default answer is **no pattern**. A pattern earns its place only by removing a pain that exists in the code today.

## Procedure

1. **Name the pain in one sentence** that points at real code: file, function, and what goes wrong on the next change. "More flexible" is not a pain. "Adding a fourth export format means editing the same `switch` in five files" is.
2. **Try the smaller fixes first**, in this order:
   - A language feature: a function value, a closure, a generator, a discriminated union with an exhaustive `switch`, or module scope.
   - A refactoring: Extract Method, Introduce Parameter Object, Decompose Conditional, Replace Conditional with Polymorphism. Use the `aipatterns-refactoring` skill.
   - A lookup table or map from key to function.
3. **Only if those fail**, find the symptom in the table below and open the candidate's reference file.
4. **Read the candidate's "Do not use it when" section before anything else.** It's collected for every pattern in [references/when-not-to-use.md](references/when-not-to-use.md). If any bullet applies, stop.
5. **Count real variants.** Fewer than three, with no concrete third planned, means you keep the conditional.
6. **Grill the application.** Before writing code, run the `aipatterns-grill` procedure: inspect the code and existing infrastructure, identify what is missing, and settle scale, performance, reliability, budget and implementation questions with the user.
7. Apply the pattern in small steps, with tests green after each step. Report which pain it removed.

## Symptom → candidate

| Symptom                                                              | Candidate                | Try first                               |
| -------------------------------------------------------------------- | ------------------------ | --------------------------------------- |
| `new Concrete()` in code that shouldn't choose the type              | Factory Method           | A factory function                      |
| Matched families of objects (all-AWS or all-GCP clients)             | Abstract Factory         | Two real families must exist            |
| Constructor with many optional params, or multi-step construction    | Builder                  | An options object or named args         |
| Many near-identical copies of an expensive object                    | Prototype                | `structuredClone` or a copy constructor |
| One instance that must be reachable everywhere                       | Singleton                | Pass the instance in (DI)               |
| Third-party interface doesn't fit                                    | Adapter                  | A wrapper function                      |
| Two independent axes of variation multiplying subclasses             | Bridge                   | Confirm both axes really vary           |
| Tree where leaves and groups behave the same                         | Composite                | A recursive function over data          |
| Add logging, caching or retries around an object                     | Decorator                | A higher-order function or middleware   |
| Callers wire many subsystem objects for one task                     | Facade                   | One module-level function               |
| Millions of objects sharing most data, with measured memory pressure | Flyweight                | Measure first                           |
| Lazy loading, access control, remote calls                           | Proxy                    | A lazy getter or language `Proxy`       |
| Pipeline of handlers, any of which may stop the request              | Chain of Responsibility  | An array of functions                   |
| Queue, log, retry or undo actions                                    | Command                  | A closure plus its inputs               |
| Callers depend on a collection's layout                              | Iterator                 | Built-in iterators or generators        |
| Components call each other in a tangle                               | Mediator                 | An existing event bus                   |
| Undo snapshots without exposing internals                            | Memento                  | Immutable state                         |
| Others must react to changes                                         | Observer                 | Built-in events, signals or streams     |
| `switch (this.status)` repeated across methods                       | State                    | A transition table                      |
| A `switch` picks an algorithm                                        | Strategy                 | Pass a function                         |
| Subclasses copy a workflow and differ in steps                       | Template Method          | A higher-order function with hooks      |
| New operations over a stable node set                                | Visitor                  | Pattern matching over a union           |
| Remote call that can hang or fail in episodes                        | Timeout, Circuit Breaker | Platform deadlines and retry settings   |
| Retries keep hammering a dependency that stays down                  | Circuit Breaker          | A failure cap and a cool-off            |
| One client's burst starves the other clients                         | Rate Limiting            | A per-client queue with a budget        |
| Expensive reads dominate, slightly stale answers are fine            | Cache-Aside              | The platform cache plus a TTL           |
| A step spans services and may fail halfway through                   | Saga                     | One local transaction                   |
| A DB write and an outbound message must commit together              | Transactional Outbox     | Both in the same transaction, no relay  |
| Webhooks or queues may deliver the same message twice                | Idempotent Consumer      | Handlers that tolerate repeats          |
| Reads and writes fight over one model, measured                      | CQRS                     | A denormalized query or a view          |
| A legacy system must shrink without stopping                         | Strangler Fig            | A rewrite behind a facade, one cutover  |

## Look-alikes: decide by intent

- **Strategy vs State:** the client chooses a strategy from outside. A state object switches itself.
- **Decorator vs Proxy:** a decorator adds behavior and stacks. A proxy controls access to one subject.
- **Adapter vs Facade:** an adapter fits one interface to another. A facade simplifies many objects.
- **Factory Method vs Abstract Factory:** one product vs a family of products.
- **Command vs Strategy:** a request to run later vs a way to compute now.

## Red flags in your own plan

- You are creating an interface with exactly one implementation "for testability". Most languages can stub without it.
- The pattern name appears in a class name (`PaymentStrategyFactoryImpl`). Name things for the domain instead.
- The change touches more files than the pain it removes.
- You can't write a test that the pattern makes easier.

## References

- [references/choosing-a-pattern.md](references/choosing-a-pattern.md): the full decision guide with flowcharts.
- [references/when-not-to-use.md](references/when-not-to-use.md): every pattern's exclusions in one file.
- [references/criticism-of-patterns.md](references/criticism-of-patterns.md): where patterns go wrong.
- [references/factory-comparison.md](references/factory-comparison.md): factory variants side by side.
- Full entries for every pattern: install `aipatterns-design-patterns`, or call the MCP server at `https://aipatterns.dev/mcp` (tools `recommend`, `get_entry`).
