Originally published on phpdesignpatterns.dev — part of the PHP design patterns course. View this lesson on the original site.

Choosing patterns with restraint

A pattern is a tool. Like any tool, it earns its keep when the job actually needs it, and gets in the way when you reach for it too early. Before we walk through the three families of patterns in the next three lessons, let's set the bar honestly.

When a pattern earns its place

Reach for a pattern when the cost of the pattern is less than the cost of the code you'd write without it. That's the whole rule. The costs of a pattern are real:

  • More files for the reader to open.
  • Indirection: the work happens through an interface, not in front of you.
  • A naming burden: every new class needs a sensible name.
  • A small mental tax for new joiners until they recognise the shape.

Those costs are worth paying when:

  • The variation, the growth, or the coupling you're solving is already painful — not hypothetical.
  • The shape of the change is going to keep recurring — not a one-off.
  • The pattern matches the problem closely (a Strategy-shaped problem really does want Strategy, not Visitor).
  • The team will recognise the pattern by name so the indirection isn't mysterious.

When a pattern is overkill

Reach for the simpler shape — a function, a match, a config array, a private method — when:

  • There are only two or three branches and they're each one line.
  • The variation lives in exactly one place and shows no sign of spreading.
  • The pattern would introduce the indirection you're worried about, not remove existing pain.
  • You're applying the pattern because it has a famous name, not because the code is hurting.

A useful self-check: if I delete the pattern and write the obvious procedural version, does the code get worse, or does it get clearer? If clearer, the pattern wasn't earning its place.

The three signals that say "a pattern fits here"

Across every pattern in this course, the same three signals show up again and again:

  1. Variation. The same operation needs to behave several genuinely different ways. Strategy, State, Template Method, Visitor.
  2. Growth. The code keeps acquiring new branches, new types, or new collaborators, and each addition forces edits to existing code. Factory Method, Abstract Factory, Chain of Responsibility, Specification.
  3. Coupling. Two parts of the system know too much about each other and you can't change one without breaking the other. Adapter, Facade, Mediator, Observer, Repository.

If you can name which of those three pressures is hurting you, you've already narrowed the field of useful patterns to a handful. Most of the work of using patterns well is diagnosing the pressure, not memorising the catalogue.

What this course optimises for

The course is structured around two beliefs:

  • Patterns are best taught from the pain. Every chapter starts with code that hurts in a specific, recognisable way before showing the pattern. If you can't feel the pain, the pattern will look like ceremony.
  • Restraint is half the skill. Every chapter has a "when this pattern is overkill" lesson. That's not filler; it's the lesson most pattern books skip, and it's the one that protects your codebase from cargo-cult applications of GoF.

The next three lessons walk through the three families patterns are usually grouped into — creational, structural, and behavioural — and tell you, family by family, what kind of pressure each one was invented to relieve.

From PHP design patterns — Introduction to design patterns