Essay / Software Engineering
Design Patterns Are Vocabulary, Not Architecture
Strategy, Adapter and Observer help name design choices. Their value depends on the forces they address, their costs and the boundaries they preserve.
A diagram can contain Strategy, Adapter and Observer and still leave the most important design question unanswered: which part of the system is responsible for deciding what a product costs?
Pattern names can make a conversation faster. They can also make a vague design sound more finished than it is. Recognizing a familiar arrangement of classes does not establish that the boundaries, dependencies or operational responsibilities are sensible.
A pattern is useful when it compresses a conversation, not when it expands the code.
I like that test because it puts the emphasis on communication. A pattern should help explain a recurring problem and the forces around a solution. If introducing it requires several extra abstractions without making a real decision clearer, the vocabulary may be doing more work than the design.
A useful name carries a reason
Consider a catalogue pipeline that prepares descriptions for several marketplaces. Each destination has different formatting rules. A developer could describe the design by walking through every conditional, or say that destination-specific formatting sits behind interchangeable strategies.
The second explanation is useful if it tells us where variation lives and what stays stable. It is much less useful if the interface contains dozens of methods that every destination implements differently, with exceptions leaking into the caller.
The Strategy description from Refactoring Guru presents interchangeable algorithms behind a common interface. In the catalogue example, the design question is whether destination formatting really shares a coherent contract. Naming it Strategy cannot answer that question for us.
A reasonable contract might take a normalized product and produce a destination listing or explicit validation errors. It should describe inputs and outcomes that the caller can understand without knowing every marketplace rule. If one destination instead changes the authoritative product record, it is performing another responsibility and deserves separate treatment.
Strategy is not a reason to invent variation
Suppose a small importer has one stable normalization rule. Wrapping it in an interface, factory and registry may make a future extension conceivable, but it also makes the current behavior harder to follow. Future flexibility has a present cost.
I would start with a direct function if the variation is hypothetical. When a second implementation appears, compare the actual differences. Perhaps both rules share a contract. Perhaps the second workflow exposes a different business concept that should not be forced into the first abstraction.
A common mistake is to predict variation too early and then preserve the prediction as if it were a requirement. The abstraction becomes difficult to remove because other code has already been organized around it. The original reason, meanwhile, was only that another strategy might someday be useful.
The practical review question is simple: which existing changes become safer or more local because this boundary exists? An answer involving current behavior is stronger than a list of imagined future integrations.
An Adapter translates meaning as well as shape
An external feed might call a field available, while an internal model distinguishes physical stock, reserved stock and quantity available to promise. Renaming the field is easy. Deciding which internal concept the supplier means is the real work.
An Adapter can provide a useful boundary for this translation. It keeps supplier-specific representation out of the rest of the application. But it should also make unsupported assumptions visible. Missing currency, ambiguous units and undocumented availability values should not become invented defaults simply because the target type requires a value.
In a proposed implementation, the adapter might return either a valid normalized record or a collection of reviewable issues. The calling workflow can then decide whether to quarantine the record, preserve the previous value or request clarification. Those are domain policies, not formatting conveniences.
A clean interface is not evidence that the translation behind it is truthful.
That distinction connects directly to product-data work. An integration can be elegant at the type level while quietly changing the meaning of the catalogue. Put the relevant assumptions in examples and tests where a reviewer can inspect them.
Observer needs a lifecycle story
Observer-style notification is attractive when several consumers react to one change. A document viewer, for example, may need to update page position, button availability and loading status as its state changes. Notification can reduce direct knowledge between participants.
It can also make control flow harder to follow. If a change triggers several listeners that each trigger more changes, understanding one user action requires reconstructing a chain of callbacks. A pattern that reduced coupling in one dimension has increased coordination work in another.
Ask who subscribes, who unsubscribes and what happens when a participant disappears. For a browser component, leaving listeners or observers active after closing a heavy view can retain resources and produce updates to stale state. Cleanup is part of the design rather than a cosmetic final step.
Also distinguish an in-process notification from a durable business event. A callback firing inside one browser session does not provide persistence, acknowledgement or replay. Moving the same word, “event,” into a distributed system introduces responsibilities that the object-level pattern does not solve.
Architectural decisions sit at another scale
Whether a site should be static or require a production service is an architectural decision. Whether a renderer uses a small state object or an observer internally is a more local design choice. Both matter, but they constrain different things.
For this publication, static generation establishes where content is assembled, what the host must execute and how updates become public. No arrangement of class patterns can compensate for choosing a runtime architecture whose maintenance burden does not fit the project.
The architecture essay discusses trade-offs in those larger choices. A pattern can support an architecture by making a boundary clearer, but it does not select the quality attributes the system should prioritize.
This is why a design review should sometimes ignore the class diagram temporarily. Identify data ownership, failure boundaries, deployment units and recovery responsibilities first. Then ask whether the local abstractions make those decisions easier to enforce. The order prevents an attractive implementation sketch from becoming the explanation for a decision nobody actually made.
Write down the forces before choosing the label
A useful design note can be short. For the hypothetical marketplace formatter, I would record that destinations change independently, callers need consistent error reporting, and product normalization should remain destination-neutral. Those forces explain the proposed boundary.
Then record costs. Adding a destination requires implementing and testing the contract. Shared behavior should not be copied into each formatter. Destination-specific exceptions must not gradually turn the common interface into a catalogue of special cases.
A small comparison can expose the decision more clearly than naming the winner immediately:
| Arrangement | Useful when | Cost to watch |
|---|---|---|
| Direct function | One coherent, stable behavior | Conditional growth as real variation appears |
| Strategy boundary | Several interchangeable behaviors | Interface drift and unnecessary indirection |
| Separate workflow | Responsibilities differ substantially | Some deliberate duplication at the edges |
These are choices within this example, not a universal ranking. A team should be able to explain why its current forces favor one row and what change would justify reconsidering it.
A pattern should survive plain-language explanation
Try removing the pattern names from a design proposal. Can a colleague still understand the responsibility of each part, how data moves and what happens when an input cannot be handled? If not, the terminology may be covering gaps in the explanation.
The reverse exercise is useful too. After explaining the design plainly, add the pattern name where it saves time for readers who know it. That makes the vocabulary an aid rather than an entry requirement.
Patterns also need an exit route. If a formerly variable behavior becomes uniform, the surrounding machinery may no longer earn its keep. Removing it can improve the design without rejecting the original decision. Good abstractions have a context; they do not deserve permanent residence because they once matched a textbook diagram.
When reviewing code, I would rather hear “this boundary keeps supplier-specific meanings out of our pricing rules” than “we used three patterns.” The first statement identifies a consequence we can inspect. The second counts vocabulary. Useful design should give the next maintainer fewer surprises, even when the implementation has fewer impressive names.