DokaLab
  • About
  • Blog
  • Contact
DokaLab
  • About
  • Blog
  • Contact

© 2026 DokaLab · Soho, London · No tracking, no sign-ups.

Set in Inter, JetBrains Mono, and Bricolage Grotesque.

AboutContactPrivacy

Engineering›Paradigm

Patterns first, or problems first?

2026-05-0712 min
  • Design Patterns
  • GoF
  • OOP
  • Software Philosophy
  • Theory Building

The Question that landed

I was deep in code one afternoon when something snagged me. Two pieces of code, both of which I'd happily call well-written. One has five GoF design patterns set into it. The other has none. How can both be right?

Surprisingly hard to explain in one line. "Good engineers wrote it" is a tautology. "They only used patterns where needed" is accurate but thin. Where exactly does "needed" live? Where does that judgement come from?

When I first wanted to write good OOP, the path I was handed was straightforward. Open the GoF book. Memorise 23 patterns. Memorise the mapping from situation to pattern. Then look for where they fit in your own code. Lectures, blog posts, code reviews. All aimed the same way. "You should drop a Strategy in here." "Wouldn't a Factory fit better?" The pattern comes first, and the code gets shaped around it.

There's a layer worth flagging here before we go on. OOP and design patterns aren't actually at the same level. OOP is a paradigm, and the GoF patterns are a catalogue that grew inside it. At the learning stage though, the two words arrive bundled together, and 'using OOP well' starts to mean 'showing patterns from the catalogue.' The learning path I described above is exactly that drift in action. You start at 'I want to write good OOP' and somehow end up at 'I should memorise 23 patterns.'

The doubt in this post starts on the patterns side of that equation. If both pieces of code can be right, then maybe the order we've been learning patterns in is itself back-to-front.

Patterns first, or problems first?

Between discovery and invention

Alexander's original definition

There's a curious fact here. In the GoF book's opening chapter, "What is a Design Pattern?", the sentence the authors quote to define a pattern isn't their own. It came from A Pattern Language by the architect Christopher Alexander, published in 1977.

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, in such a way that you can use this solution a million times over, without ever doing it the same way twice.
Christopher Alexander, A Pattern Language (1977)

The key part is "describes a problem which occurs over and over again". A pattern describes a recurring problem. It's the result of observation, not invention. The GoF authors say this themselves throughout the book. They didn't invent the patterns, they catalogued recurring structures from systems that worked.

But the way we were taught ran in the opposite direction. Memorise the catalogue, then apply it. The result of discovery turned, somewhere along the way, into a tool for invention.

Thirty years on, Alexander's own critique

Looking at what Alexander said years later, the depth of that distortion becomes clearer. He gave the OOPSLA 1996 keynote and used it to criticise the software patterns movement directly.

Here's how he put it.

The pattern concept, for you, is an inspiring format that is a good way of exchanging fragmentary, atomic, ideas about programming. Have you asked whether a particular system of patterns, taken as a system, will generate a coherent computer program? If so, I have not yet heard about that.
Christopher Alexander, OOPSLA 1996 Keynote

What Alexander meant by "pattern" was something generative. A system where patterns combine to produce a living whole. The software world, on his reading, took that and reduced it to a catalogue of bolt-on parts. It's a thirty-year-old critique, and it still lands.

Archives: Keynote Speech to the 1996 OOPSLA Conventionwww.patternlanguage.com

The direction of the arrow

Step back a level and a piece of philosophical vocabulary fits well. The phrase is "direction of fit", introduced by Anscombe in 1957. Belief flows world to mind. The mind has to match the world; if it doesn't, you correct the mind. Desire goes the other way. Mind to world. If they don't match, you change the world.

Patterns were always meant to be a belief-like act of recognition. You look at well-written code and think, "oh, that's a Strategy." You're putting a name on a structure that's already there.

Pattern-first learning flips it into desire instead. The shape of Strategy lives in your head first, and you bend the code to fit it. When it doesn't fit, you twist the code harder. What was supposed to be recognition has turned into enforcement.

Same shape, different origins

Two interfaces

Forced and emergent patterns can produce nearly identical code at the surface. The origins are different though, and given time they grow into different shapes.

Here's a worked example. Imagine you need pricing logic in some domain, and there are two ways you might end up reaching for an interface.

TypeScript
1234567891011121314
TypeScript
123456789101112131415

On the surface they look almost identical. Interface plus implementations. Origins are different though. In A, the axis of change is imaginary, and the implementation count stays at one forever. The interface is sitting there like an insurance policy, but no claim ever gets filed. In B, the axis of change is real. B2C and B2B are genuinely different algorithms, and there's a sensible reason to bind them under the same contract.

A week later they still look the same. Six months in they're different beasts. A is fossilised. One interface for an imagined future, one implementation forever. B keeps growing as the billing domain grows. New ways of getting paid arrive (government procurement, marketplace settlement, that sort of thing), and the same contract absorbs them.

There's a conversation test that makes the difference obvious.

Q: Why is there a Strategy here? A1: "It's the Strategy pattern." A2: "There are two billing methods that share a contract, so this fits."

Case A2's answer never names a pattern. The author could have arrived at the same structure without knowing what a "Strategy" is. That's the signature of emergent.

Those who plant patterns, those who don't

That's the technical layer. The real problem though is social.

Plant a pattern and the "evidence of design" is visible from outside. There's a class diagram you can draw. Interface names, directory structure. In code review someone says "nicely designed." Your CV gets a line like "experience with Strategy, Factory, Observer."

Skip the pattern and well-written code becomes invisible from outside. Just a handful of plain functions, and all the careful decisions live in the author's head. You're more likely to hear "why didn't you make an interface?" Then "because there's no real axis of change" is the precise answer, but it lands flat. The listener has to be at roughly the same stage of learning for it to register.

Planting a pattern is buying expensive insurance, and skipping it is self-insurance. Self-insurance only works if you've got the capital.

Capital here means understanding the domain, having run similar systems before, having an instinct for how often things change picked up from conversations with operators or product folks. Without that capital, skipping the insurance bites you when the actual risk hits. So buying expensive insurance until the capital builds up is reasonable. That stage is a natural part of learning, not something to look down on.

What you don't want is to keep buying expensive insurance after the capital is there. That's where over-engineering lives. The transition is hard to make because external assessment dips for a while. You hear things like "they used to plant these slick patterns, now they're just writing functions."

Others making the same point

This doubt isn't new. People in other fields have arrived at the same core point, in their own time and vocabulary.

Hickey on simple and easy

Rich Hickey, in his 2011 talk Simple Made Easy, draws an etymology distinction worth borrowing. "Simple" comes from sim + plex, "one fold, one twist". "Easy" comes from the Latin adjacent, "lying near, familiar". These are different axes. Simple is objective (is it tangled or not). Easy is subjective (is it familiar to you).

Complect... it means to interleave or entwine or braid.
Rich Hickey, Simple Made Easy (2011)

Pattern-first thinking confuses easy with simple all the time. You plant a pattern because it's familiar (easy), and you tell yourself that makes the code simple. Forcing a familiar abstraction onto a domain is just adding another braid. The opposite of simple.

Naur on code as the lossy surface of a theory

Peter Naur's 1985 paper Programming as Theory Building gets at something deeper. The output of programming isn't the code, it's the theory that lives in the programmer's head. Code is only a lossy expression of that theory.

What characterises intellectual activity, over and beyond activity that is merely intelligent, is the person's building and having a theory, where theory is understood as the knowledge a person must have in order not only to do certain things intelligently but also to explain them, to answer queries about them, to argue about them.
Peter Naur, Programming as Theory Building (1985)

Through Naur's lens, a forced pattern is code with the form planted and no theory behind it. The author's head has no answer to "why this interface?", so when someone else picks up the code six months later they can't reconstruct that theory. In Naur's terms, the code was already dead the moment it was written.

An emergent pattern is the opposite. The author's head holds the theory "B2C and B2B are genuinely different algorithms", and the interface is one expression of that theory. The theory is alive, so the code is alive.

Alexander, the GoF themselves, Hickey, Naur. All of them, in different decades and different fields, made roughly the same point. What we memorised was the surface of these insights, the catalogue. The grain underneath the surface was largely missing from the way we were taught.

Back to learning order

If you were going to redesign the learning path, what would it look like? What I keep coming back to looks roughly like this.

1.

Principles first. Cohesion, coupling, invariants, boundaries, information hiding. The names are abstract, but they all sit on one question: where does the unit of change flow from and to.

2.

Write things. Take those principles into your head and solve actual problems. Don't be conscious of pattern names. Let whatever structure emerges just be there.

3.

Run things. Operate that code for six months to several years. Feel directly which abstractions paid back the insurance and which only kept charging the premium.

4.

Then open the pattern book. At that point the book reads differently. It works as a vocabulary list rather than a catalogue. "Oh, that shape I planted back then is what someone once called Template Method."

Patterns aren't invented, they're discovered. They aren't a catalogue, they're a vocabulary.

That's why I don't recommend the pattern book to juniors. If I do recommend it, the line that comes out is "don't read this yet. Operate something for six months and then open it. Otherwise the book reads wrong."

I don't want to land this in a definitive tone. I went through stages one and two honestly myself. First I memorised the pattern book. Then I planted patterns everywhere. Whole codebases packed with interface plus single-implementation pairs. As that code ran for six months to a year, some interfaces actually accepted a second implementation and paid back the insurance. Others stayed fossilised with one implementation forever and just kept charging the premium. At some point I started seeing where the difference came from.

After that, a knife came up automatically when I wrote new code. "Is there a real axis of change here?" Once you start answering that question honestly, the hand that reaches for patterns slows down on its own.

Looking at code I'm writing now, sometimes I can attach a pattern name to a structure after the fact. "Oh, this is Template Method." When I built it, I went with the shape "common skeleton plus a hook for variation" because it felt natural. Only later did I notice someone had put a name on that shape long before. The sense that this is the right direction has been settling in over time.

When I first wanted to write good OOP, the question I asked was "which pattern should I use here?" The question I ask now is "does this need a pattern at all?" That one-line difference flips the direction of the learning arrow.

...is how I wanted to close it, in a definitive tone, except that's the very tone this whole post has been picking apart. I sat with the last line for a while, then went back to the two pieces of code at the start. Five patterns on one side, zero on the other. Not as the answer, but as a reminder that this is still where the doubt begins.

Contents

  • The Question that landed
  • Between discovery and invention
  • Alexander's original definition
  • Thirty years on, Alexander's own critique
  • The direction of the arrow
  • Same shape, different origins
  • Two interfaces
  • Those who plant patterns, those who don't
  • Others making the same point
  • Hickey on simple and easy
  • Naur on code as the lossy surface of a theory
  • Back to learning order