Skip to content
Go back

Design Patterns Are Solutions, Not Standards

Published:

I’ve seen the same pattern play out repeatedly: a team adopts a design pattern because it feels like the “right” thing to do, and within six months it’s embedded so deeply that nobody questions it anymore. The pattern becomes the architecture. Except it isn’t.

The Pattern Isn’t the Architecture

A design pattern solves a specific problem. That’s it. Using one doesn’t mean you have an architecture, it means you’ve picked a tool. An architecture is the set of decisions you make about how your system fits together — the boundaries, the constraints, the trade-offs. A pattern is one possible implementation detail within that bigger picture.

The confusion happens when people reverse the relationship. They start with the pattern and assume the architecture will emerge around it. It doesn’t. What emerges is a codebase shaped by the pattern’s constraints, not by the actual requirements of the system.

MediatR Is a Good Example of This

MediatR is everywhere in .NET projects, and I understand why — it’s clean, it’s testable, and it makes request handling feel structured. But here’s what it actually does: it decouples request dispatch from handlers. That’s it. It’s excellent for vertical slices where each command or query is independent.

What it doesn’t do is solve horizontal concerns. Cross-cutting logic, shared behaviour, consistency across the codebase — none of that comes from MediatR. People assume that adding it means they have CQRS. They don’t. They have a message bus.

The result is a CRUD app with unnecessary indirection and the illusion of structure. Every simple operation now goes through a pipeline, and new developers spend their first week wondering why a straightforward data update requires three files and a handler registration. The complexity wasn’t earned — it was inherited from the pattern.

When Cargo-Culting Goes Wrong

Patterns get adopted because someone senior used them on a previous project, or a YouTube tutorial made them look clean and professional. Nobody questions whether the problem the pattern solves actually exists in their codebase. It just becomes “how we do things here.”

Once that happens, the pattern shifts from a considered choice to a standard. New developers learn the pattern, not the reasoning behind it. They don’t know what problem it was meant to solve because in this codebase, that problem never existed. The cycle continues.

I’ve seen this with Repository pattern wrapping EF Core, Singleton services that could’ve been static classes, and Factory patterns for objects that never vary. Each one added because it felt like good practice, not because the system needed it.

Understand the Problem First

Before reaching for a pattern, you should be able to articulate the specific problem it’s solving in this codebase. Not in the project you came from, not in the codebase you read about — here, in the system you’re building.

If you can’t explain why the pattern fits, it probably doesn’t. Simple problems deserve simple solutions. Complexity should be earned, not assumed. The right question isn’t “should we use X pattern?” — it’s “what problem are we actually solving, and is this pattern the simplest way to solve it?”

Sometimes the answer is yes. Often it isn’t. A direct method call might be enough. A shared utility class might do the job. The pattern might be overkill. If you’re adding it because it feels like the right shape rather than because it solves a real problem — that’s the signal to stop.

Patterns Should Never Become Standards

A standard implies it applies universally. Patterns don’t. Every pattern has trade-offs — performance, readability, maintainability, cognitive load. Standardising a pattern means inheriting those trade-offs everywhere, whether they’re appropriate or not.

Tech leads have a responsibility here. Challenge pattern choices. Ask what problem is being solved. Push back when a pattern is introduced because it “looks professional” or “is what we did last time.” Patterns should be justified, not assumed.

The best architectures I’ve seen aren’t the ones that religiously apply patterns — they’re the ones where each decision is deliberate, where complexity only exists when it’s needed, and where the system is shaped by its actual requirements, not by what felt familiar.

Conclusion

Patterns are one of the most useful tools in software development. They give us solutions to problems that have already been solved, and that’s genuinely valuable – when the problem exists.

The issue here isn’t using patterns, it’s treating them as your default. When that happens, the reasoning behind using it disappears, but the complexity stays. New developers inherit it with no context, and the codebase forms to the decisions made that no one can explain.

Keep the patterns. Lose the cargo-culting. If you can’t articulate what problem a pattern is solving in your specific codebase, that’s worth stopping and questioning before it gets baked in.

I’d be interested to hear whether this matches your experience — particularly if you’ve had to unpick a pattern that outlived its usefulness. Feel free to reach out on LinkedIn.


Share this post on:
Matt Thomas

Matt Thomas

Azure Solutions Architect at Howden · Microsoft Certified AZ-305


Next Post
Hosting Static Sites for Free with Cloudflare Workers