Why we still recommend Design Patterns
The Gang of Four book is not a catalog of classes to copy. It is a vocabulary for discussing responsibilities, coupling, variation, and composition. More than thirty years later, languages and frameworks do more of the mechanical work; deciding where each change belongs is still the hard part.
From uncertain change to maintainable design
- Likely changerule · provider · channel
- Responsibilitywho decides · who executes
- Boundarycontract · dependency · state
- Compositionpolicy · adapter · middleware
- Testingcases · substitution · isolation
- Evolutionlocalized change · less risk
Short summary
Design Patterns documents recurring solutions for designing objects that collaborate without being tied to one concrete implementation. Its value is not memorizing names. It is recognizing forces: what changes, what must remain stable, who owns a decision, and where a boundary belongs.
- A pattern describes a relationship and a trade-off, not a universal recipe.
- Composition often varies behavior without creating rigid inheritance trees.
- Interfaces protect decisions that are likely to change.
- The design should remain simpler than the problem it solves.
Patterns are still useful
Patterns are still useful because systems still integrate providers, business rules, state, and workflows that change at different rates. What changed is how we express them: a function, module, TypeScript interface, injected dependency, or middleware can play the role the book demonstrates with class hierarchies.
- Strategy encapsulates an interchangeable policy: pricing, validation, or route selection.
- Adapter translates an external contract without leaking its API into the domain.
- Decorator adds logging, metrics, caching, or authorization around an operation.
- Observer expresses notification and events, but still needs boundaries for state, ordering, and delivery.
How it applies today
Applying the book today starts by finding the change point, not by choosing a pattern. In TypeScript it may be a first-class function or a small interface; in Java, an injected implementation; in a web system, an infrastructure adapter or middleware chain. The pattern name helps the team discuss the design, but the code should use the most direct mechanism that keeps the contract clear.
- Separate business policies from SDKs, databases, and frameworks.
- Use composition and dependency injection to substitute implementations in tests and production.
- Treat events as operational contracts: duplicates, ordering, retries, and failure are part of the design.
- Let the language do the work: closures, modules, algebraic types, and pattern matching may beat a class hierarchy.
When not to use them
A pattern applied before understanding the problem creates indirection, names, and extension points nobody needs. If there is only one variant, a simple function may beat Strategy. If a framework already provides a stable mechanism, wrapping it in five classes can hide the intent. The book is a toolbox, not an architectural obligation.
- Do not introduce a pattern to demonstrate knowledge of the catalog.
- Do not abstract a hypothetical variation without evidence it will arrive.
- Do not confuse more interfaces with better design.
- Remove the abstraction if the code becomes harder to read, test, or debug.