Skip to content
aipatterns.dev

Guide

What Is a Design Pattern

A 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.

Also known as
Design pattern, Pattern

Definition

A design pattern is a description of how to arrange classes, objects or functions to solve a problem that keeps coming back in different programs. It is not code you copy. It is a shape you adapt.

A pattern names a recurring problem, the forces that make it hard, and a solution structure that balances those forces.

Think of a pattern as a blueprint for a kind of building, not the building itself. Two teams applying Observer will write different code, in different languages, with different names. A reader who knows the pattern still recognizes both.

Pattern vs algorithm vs library

ThingWhat it gives youExample
AlgorithmExact steps that produce a resultBinary search, Dijkstra
LibraryWorking code you calllodash, java.util.concurrent
PatternA structure of roles and relationshipsStrategy, Adapter
IdiomA language-specific habitPython context managers, Go defer

An algorithm tells you what to compute. A pattern tells you who should know about whom. You can implement the same algorithm with or without a pattern, and the same pattern around many algorithms.

What a pattern description contains

Every pattern page on this site follows the same structure, because an agent should find the same facts in the same place:

  • Intent: the problem and the solution in two or three sentences.
  • Forces: the competing pressures. Without forces, a pattern reads like a rule; with them, you can tell when it does not apply.
  • Therefore: the solution in one sentence.
  • Structure: the participants (roles) and how they connect, as a class diagram.
  • Use it when and Do not use it when: concrete triggers, and the simpler alternative for each misfit.
  • Example: one realistic scenario in four languages.
  • Trade-offs and Relations: what you pay, and which neighbors to compare against.

The "Do not use it when" section matters most. See Using this reference as an agent.

Roles, not classes

A pattern describes roles. In Strategy, the roles are context, strategy interface and concrete strategy. One class can play more than one role, and a role can be played by something that is not a class at all:

TypeScript
// The "strategy" role played by a plain function type.
type PriceRule = (subtotal: number) => number;

const tenPercentOff: PriceRule = (s) => s * 0.9;
const noDiscount: PriceRule = (s) => s;

function checkout(subtotal: number, rule: PriceRule): number {
  return rule(subtotal);
}

checkout(200, tenPercentOff); // 180

There is no interface keyword and no class here. The pattern is still present: a caller delegates a varying step to an interchangeable value. Recognizing patterns by role instead of by syntax keeps you from adding ceremony that the language already makes unnecessary.

Levels of scale

Patterns exist at several sizes:

  • Idioms: tied to one language. Not covered here.
  • Design patterns: a handful of collaborating objects. The 22 entries in this catalog.
  • Architectural patterns: the shape of a whole system, such as layered architecture, ports and adapters, or event sourcing.

The classic catalog sorts design patterns into three groups by purpose. See Classification of patterns, or browse creational, structural and behavioral directly.

A worked recognition

You find this in a codebase:

TypeScript
class ReportExporter {
  export(report: Report, format: "csv" | "pdf" | "xlsx"): Buffer {
    if (format === "csv") { /* 40 lines */ }
    else if (format === "pdf") { /* 60 lines */ }
    else { /* 50 lines */ }
  }
}

The problem is recurring (pick one of several algorithms at runtime). The forces are present (new formats keep arriving, each branch is hard to test alone). That matches the intent of Strategy. It also matches the Switch Statements smell, and the refactoring path is Replace Conditional with Polymorphism.

If there were only two formats and no third in sight, the right call is to leave the if alone. A pattern is a response to forces, not a goal.

Key points for agents

  • Match on the problem and forces, never on surface keywords in the request.
  • A pattern is a vocabulary item. Naming it in a commit message or comment ("adapter for the legacy billing API") tells the next reader a lot in a few words.
  • Prefer the smallest structure that plays the needed roles. Functions, closures and modules count.
  • Read Choosing a pattern before introducing one, and Criticism of patterns to know the failure modes.

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.