| name | design-patterns |
| description | Use this skill whenever the user mentions any pattern by name, any code that smells like it wants a pattern, asks about structure, coupling, or abstraction โ even if they don't say "design pattern". Don't wait to be asked explicitly. Trigger on: giant if-else chains on type, telescoping constructors, "how do I add behavior without subclassing?", "this is getting messy", "how do I decouple this?", "is this a Strategy or a State?", mentions of GoF, PEAA, Hexagonal Architecture, Ports & Adapters, DDD tactical patterns, or any named pattern. Also triggers when code shows a pattern applied incorrectly, unnecessarily, or when a simpler pattern would serve better than what the user reached for. |
Design Patterns
Identify the right pattern for a problem, explain it precisely, and apply it with minimal disruption to the existing codebase. Patterns are vocabulary for communicating structure โ not solutions to be force-fitted.
Skill workflow โ patterns often follow from structural review:
refactoring (prepare the ground for a pattern) โ design-patterns (apply the pattern) โ adr (record the architectural decision)
Philosophy
Patterns are named solutions to recurring design problems in a given context. Their value is not the solution itself โ it's the name. When you say "Decorator", everyone on the team knows the structure, the intent, and the tradeoffs. Bad pattern usage happens in two ways:
- Over-application โ using a pattern because it's a pattern, not because it solves a real problem. The Pattern Astronaut disease.
- Misidentification โ implementing something that looks like a pattern but violates its invariants (e.g., a "Factory" that also manages object state โ that's not a Factory).
"Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem." โ Christopher Alexander (precursor to GoF)
Pattern Catalog
Creational Patterns
| Pattern | Intent | Use when |
|---|
| Factory Method | Define an interface for creating an object, let subclasses decide the type | You need to decouple object creation from the creator; creation logic may vary by subclass |
| Abstract Factory | Create families of related objects without specifying concrete classes | You need multiple related objects that must be consistent (e.g., UI theme components) |
| Builder | Separate construction of a complex object from its representation | Object requires many optional parameters; telescoping constructors are getting unwieldy |
| Prototype | Create objects by cloning existing instances | Creation cost is high; objects are configured at runtime and cloning is cheaper |
| Singleton | Ensure a class has only one instance with global access | Shared resource that must be coordinated (use sparingly โ it's global state) |
Structural Patterns
| Pattern | Intent | Use when |
|---|
| Adapter | Convert an interface into another interface clients expect | Integrating incompatible interfaces; wrapping a legacy or third-party API |
| Bridge | Decouple abstraction from implementation so both can vary independently | Avoiding a class explosion when you have two dimensions of variation |
| Composite | Compose objects into tree structures to treat individual and group uniformly | Working with tree structures where leaves and composites share an interface |
| Decorator | Attach additional responsibilities to an object dynamically | Adding behavior without subclassing; behaviors should be combinable |
| Facade | Provide a simplified interface to a complex subsystem | Reducing coupling to a complex subsystem; simplifying a common usage path |
| Flyweight | Use shared state to efficiently support a large number of fine-grained objects | Large numbers of similar objects with shared state (e.g., characters in a text editor) |
| Proxy | Provide a surrogate that controls access to another object | Lazy initialization, access control, logging, caching around an object |
Behavioral Patterns
| Pattern | Intent | Use when |
|---|
| Chain of Responsibility | Pass a request along a chain of handlers until one handles it | Multiple objects may handle a request; handler is not known a priori |
| Command | Encapsulate a request as an object | Parameterize operations, support undo/redo, queue requests |
| Iterator | Provide sequential access to elements without exposing representation | Traversal of a collection without exposing its internals |
| Mediator | Define an object that encapsulates how objects interact | Many-to-many communication between objects; reducing direct dependencies |
| Memento | Capture and restore an object's internal state | Implementing undo; snapshotting state without violating encapsulation |
| Observer | Define a one-to-many dependency so observers are notified automatically | Event-driven systems; decoupling publishers from subscribers |
| State | Allow an object to alter its behavior when its internal state changes | Object behavior depends on state and must change at runtime |
| Strategy | Define a family of algorithms and make them interchangeable | Multiple algorithms for the same operation; selecting algorithm at runtime |
| Template Method | Define the skeleton of an algorithm, deferring steps to subclasses | Invariant parts of an algorithm in a base class; variant parts in subclasses |
| Visitor | Separate an algorithm from the object structure it operates on | Adding operations to objects without modifying them; double dispatch |
Enterprise Application Patterns (Fowler's PEAA)
| Pattern | Intent |
|---|
| Repository | Mediate between domain and data mapping layers using a collection-like interface |
| Unit of Work | Maintain a list of objects affected by a business transaction |
| Identity Map | Ensure each object is loaded only once by keeping every loaded object in a map |
| Data Mapper | Move data between objects and a database while keeping them independent |
| Active Record | Object wraps a row in a database table and includes domain logic |
| Service Layer | Defines an application's boundary with a layer of services that establishes a set of available operations |
| Domain Model | An object model of the domain that incorporates behavior and data |
| Transaction Script | Organizes business logic by procedures where each procedure handles a single request from the presentation |
Hexagonal Architecture / Ports & Adapters (Alistair Cockburn)
Hexagonal Architecture (also called Ports & Adapters) is an architectural pattern โ not a GoF pattern โ that frequently comes up alongside DDD and clean architecture discussions.
Core idea: The application sits at the centre. Everything external (databases, HTTP, message queues, UIs, third-party APIs) communicates through Ports (interfaces defined by the application) and Adapters (implementations of those ports). The application never depends on infrastructure โ infrastructure depends on the application.
[ UI Adapter ] [ CLI Adapter ]
\ /
+-[ Driving Port ]-+
| |
| APPLICATION |
| |
+-[ Driven Port ]--+
/ \
[ DB Adapter ] [ API Adapter ]
| Concept | Description |
|---|
| Driving port | An interface the application exposes to the outside world (e.g., OrderService) |
| Driven port | An interface the application requires from infrastructure (e.g., OrderRepository) |
| Adapter | Concrete implementation of a port (e.g., SqlOrderRepository, RestOrderController) |
Use when: You want to test the application core without a database or HTTP stack, swap infrastructure without touching domain logic, or make the dependency rule explicit.
Pairs with: domain-driven-design skill (Hexagonal is the preferred shell for a DDD Domain Model), adr for recording the architectural boundary decision.
Process
1. Identify the problem
Before reaching for a pattern, articulate the problem precisely:
- What is varying? (creation? behavior? structure? communication?)
- What is the coupling you're trying to break?
- What invariant are you trying to enforce?
Why this matters: Patterns are solutions to named problems. If the problem isn't named first, you're matching syntax, not solving intent โ and you'll often pick the wrong pattern or apply the right pattern for the wrong reason. A pattern applied without a clear problem statement is decoration, not design.
2. Pattern recognition
Look at the existing code structure:
- Are there
switch/if-else chains on type? โ Strategy or State may be appropriate
- Are objects being constructed with many optional parameters? โ Builder
- Are you wrapping an external API to match your domain interface? โ Adapter or Facade
- Are behaviors being stacked on objects at runtime? โ Decorator
- Are you notifying multiple objects when something changes? โ Observer
3. Evaluate fit
For each candidate pattern, ask:
- Does this solve the specific problem or just look like it does?
- What does this cost? (extra classes, indirection, learning curve)
- Is the problem likely to recur in a way that justifies the abstraction?
- Would a simpler approach (a function, a closure, a plain object) do the job?
Why this matters: Abstractions have carrying costs โ extra files, indirection layers, and cognitive overhead for every future reader. A pattern is justified only when the cost of not having the abstraction exceeds the cost of the abstraction itself. Defaulting to the pattern is the same mistake as defaulting to no pattern.
4. Apply the pattern
- Make the smallest change that introduces the pattern
- Name things according to the pattern's vocabulary so intent is self-documenting
- Write tests before refactoring toward the pattern (see
tdd skill)
- Apply the pattern through incremental refactoring moves (see
refactoring skill)
5. Record the decision
If the pattern choice is non-obvious, use adr to record:
- What problem the pattern solves
- What alternatives were considered
- Why this pattern was chosen over simpler alternatives
Anti-patterns
- Pattern astronaut โ adding patterns because they're patterns, not because they solve problems. The cost of every abstraction must be justified by the problem it eliminates.
- Wrong-dimension variation โ using Strategy when the variation is on state (that's State), using Decorator when you need composition across a hierarchy (that's Composite).
- Singleton abuse โ Singleton is global mutable state with extra steps. The problem is not the single-instance guarantee โ it's that callers become secretly dependent on a hidden global, making the dependency graph invisible, tests brittle (shared state bleeds between runs), and implementations unswappable. Prefer dependency injection so dependencies are explicit and replaceable.
- Factory everything โ if construction is simple and stable, a constructor is fine. Factories add indirection that must earn its keep. Reach for a Factory when creation logic is complex, varies by context, or must be deferred โ not as a default wrapper around
new.
Scope
This skill handles: pattern identification, pattern selection, pattern application guidance, and recognizing misapplied patterns.
This skill does not handle: the mechanical refactoring steps to apply a pattern (use refactoring), recording the final decision (use adr), or domain modeling decisions (use domain-driven-design).
When done, return control to the user.