Skip to main content
Blog10 min read

Patterns travel, frameworks don't

Twenty-five years and nine production languages later, the frameworks have all turned over. The patterns underneath are what made the work cumulative.

I've shipped production code in nine languages: Go, Rust, Python, TypeScript, C#, Java, Kotlin, Swift, and the JavaScript that came before TypeScript. I started in 2001 on .NET 1.0. The languages I'm paid to write today did not exist when I began. Half of the frameworks I learned well enough to teach are now unsupported, deprecated, or quietly maintained by a handful of people on weekends.

That should be a depressing sentence. For most of the engineers I started alongside, it has been. The framework treadmill is real, and it punishes the people who treat each cycle as the thing they're actually learning. Every five years their leverage resets. The senior title carries less weight because the substrate it was earned on has been replaced.

I don't feel that. The work feels cumulative. The reason is not that I'm cleverer than the people running on the treadmill — it's that, fairly early, I made a different bet. I bet on the patterns underneath the tools. That is the canonical line in how I work: trust the patterns underneath the tools. This essay is the long version.

The bet, stated plainly

A framework is an opinion about how to express a pattern in a particular language at a particular moment. Patterns are older, slower-moving, and rarer than frameworks. Domain-Driven Design is older than most of the languages I've shipped in. CQRS is older than the cloud. Event sourcing is older than git. Reconciliation loops, the beating heart of Kubernetes and ArgoCD and the whole GitOps movement, were running inside thermostat firmware in the 1970s.

If you learn the framework, you have learned the framework. If you learn the pattern, you have learned how every future framework that touches that problem space will eventually be structured, because there are only so many ways the problem can be sensibly decomposed.

So the question I ask, when something new lands on my desk, is not what is this tool — it's which pattern is this tool an expression of. Once I can name the pattern, I usually already know where it will be strong, where it will leak, and what the next version is going to fix. The tool stops being a thing to learn and becomes a dialect of something I already speak.

The same pattern, eight times, in different costumes

The clearest way to make this concrete is to walk through a single pattern across the languages I've used it in.

Take event sourcing — the pattern where you store the sequence of facts that happened to a system, and derive state by replaying them, instead of overwriting state in place. Greg Young and Udi Dahan were teaching this at conferences in the late 2000s. I sat through Udi's Advanced Distributed Systems Design course around then, and the worldview stuck.

At Alpari, building the capital reporting platform, the same idea was running underneath the FCA-compliant ledger: every position change, every margin call, every risk breach was an event, and the reports the regulator wanted were projections over the event log rather than queries against a mutable table. The language was C#, the messaging was WCF Duplex, the UI was Silverlight with MVVM and Rx. All of that is gone now. Silverlight is dead. WCF Duplex is dead. The pattern is not gone. The pattern is in production at every fintech I've touched since.

At Credit Suisse, on the derivatives pricing platform, the same lens applied — a different language stack assimilating Python, Java, and Ruby legacy systems into a unified .NET service surface, but underneath, the trade lifecycle was a sequence of events and the pricing engine was reading projections. The hard part of the Clariden Leu merger integration was not the WCF SOAP plumbing or the Prism modules; it was reconciling two banks' event vocabularies into one. Frameworks didn't help with that. Domain-Driven Design's bounded context and context map did.

By the time I rebuilt a comparable ledger in Rust on dunnhumby's retail analytics platform, the language had nothing in common with the Alpari work. Lifetimes instead of garbage collection. tokio instead of Task<T>. Kafka instead of WCF Duplex. Yet the design conversation in the first week looked exactly like the design conversation at Alpari in 2010: what are the events, who owns the projections, where is the consistency boundary, how do we replay. The pattern carried. The pattern told me what I was building before the borrow checker told me how.

The same is true for the reconciliation loop. Kubernetes did not invent it. Kubernetes packaged it well enough that an entire generation of platform engineers met it for the first time there and assumed it was a Kubernetes thing. It isn't. A reconciliation loop is: here is the declared desired state, here is the observed actual state, compute the diff, drive it toward zero, repeat forever, never assume the previous step succeeded. Kubernetes is one implementation. ArgoCD is another. Terraform's plan/apply cycle is a coarser version. Cert-manager renewing short-lived certificates is the same idea. The custom Kubernetes operators I wrote at Philips to drive the Clinical AI App Store inside air-gapped hospital VMware estates were the same idea, in Go.

If you learn that pattern once, properly, you can move between ArgoCD, Flux, a hand-rolled operator and a Pulumi automation script in an afternoon. If you only learn ArgoCD, every other version of the same pattern looks like a new tool to invest in.

What this looked like at the start

I want to be careful not to make this sound retrospectively clean. I did not start in 2001 with a strategy. I started at DesignSquad, a small London agency, writing CRUD systems and admin dashboards on .NET 1.0 for SMEs who didn't care how the site was built. What that work taught me, slowly, was that the same shapes kept appearing: a thing has a list view, a detail view, an edit view, a save handler, some validation, an authorization check, an audit trail. The framework rotated — WebForms to MVC, ADO.NET to NHibernate, hand-rolled JS to jQuery — but the shape didn't.

By the time I got to Live Nation and led the migration from .NET 1.1 WebForms to ASP.NET MVC under TDD and CI, I had already seen enough cycles to recognise that what I was actually doing was separating concerns more cleanly — not adopting a framework. WebForms encouraged a pattern (page-as-class with viewstate) that was bad for testability. MVC encouraged a pattern (request-as-function with explicit model binding) that was good for testability. The new framework was the carrier; the pattern was the cargo. The promotion from senior to lead during that work happened, I think, because I could explain the cargo to the rest of the team in language that did not require them to learn MVC first.

That was the moment, around 2010, when I stopped tracking frameworks as the primary thing and started tracking patterns. I bought Eric Evans' Domain-Driven Design and Vaughn Vernon's Implementing Domain-Driven Design. I read Gregor Hohpe and Bobby Woolf's Enterprise Integration Patterns until it was annotated to death. I sat in Udi Dahan's course. I started reading martinfowler.com the way other people read the news. It was the cheapest investment I have ever made.

Why the pattern bet compounds and the framework bet doesn't

Here is the asymmetry. A new framework, on average, takes a competent engineer about two to six weeks to become productive in. A new pattern takes two to six months of doing the work to internalise — sometimes longer, because patterns reveal themselves slowly through the failure modes they prevent, and you have to ship long enough to see those failure modes.

That looks like patterns are the worse investment. They aren't, because the half-lives are different. Frameworks have a half-life of roughly five years; some less. Patterns have a half-life measured in decades. DDD was published in 2003 and is more relevant now than it was on the day it was written. CQRS was named in 2010 and is the default split inside almost every serious distributed system I see in 2026. Event sourcing predates them both and is the substrate of every audit-driven regulated platform I've shipped.

So the engineer who invests six months learning a pattern amortises that cost across every framework they touch in the next twenty years. The engineer who invests six weeks learning a framework amortises that cost across the next five years, and then pays again. By year fifteen, the pattern engineer has three patterns and has learned twelve frameworks fast, because the patterns gave them the scaffolding to skim each framework's documentation looking for the bits that aren't the pattern. The framework engineer has learned twelve frameworks the long way and has nothing underneath them that connects.

This is the bet behind what Martin Fowler, Unmesh Joshi and Gitanjali Venkatraman called the Expert Generalist in their 2025 piece. Their fourth characteristic — favour fundamental knowledge: patterns and trade-offs over tool-specific recipes — is the explicit, named version of what I've been doing implicitly since around 2010.

The patterns I'd put my money on

If I had to name the patterns I think will still be load-bearing in 2040, this is the list I'd defend.

Domain-Driven Design — bounded contexts, aggregates, ubiquitous language. The single highest-leverage pattern I've ever learned. It is the reason I can walk into an underwriting team at BrightInsight, a clinical workflow team at Philips, or a restaurant-operations team at Just Eat, and know that the first month's job is to learn the domain language before I touch the abstraction. Every microservice diagram I've seen go wrong has gone wrong at the bounded context boundary, not at the framework.

CQRS — separate the write model from the read models. Once you've internalised it, you stop trying to make one schema serve both regulatory queries and operational writes. The Alpari capital reporting work, the Credit Suisse pricing work, the BrightInsight indemnity DSL work — every one of them was a CQRS decomposition under the hood.

Event sourcing — store facts, derive state. Mandatory for anything regulated. Optional but usually right for anything with an audit obligation.

Reconciliation loops, declarative IaC, and GitOps. Three names for one family: declared desired state, observed actual state, drive the diff, with git as the source of truth. If you understand this family, ArgoCD and Flux and Pulumi and Terraform and cert-manager stop being five different tools and start being five expressions of one idea. The cultural shift is the same too — every change is a commit, every audit is a git log, every rollback is a revert.

Contract testing — Pact and its descendants. At dunnhumby, contract-driven development with Pact cut integration bugs by 40%. The pattern is older than Pact and will outlive Pact: encode the contract between services as an executable artefact that both sides agree to, and fail the build when either side drifts.

Hexagonal architecture / ports and adapters. The cousin of DDD that makes the infrastructure is replaceable idea structural rather than aspirational. The reason I can rewrite a service from C# to Rust without rewriting the domain model is that the domain model never knew it was talking to a framework.

Reactive streams and backpressure. Different name in every language — Rx in C# and JavaScript, tokio streams in Rust, Project Reactor in Java, asyncio in Python — same underlying pattern: producers and consumers running at different rates, with explicit flow control between them. The Alpari alerting engine ran on Rx. The dunnhumby Kafka pipelines run on roughly the same shape.

Rules as data, not as code. The work I'm proudest of is the BrightInsight indemnity DSL, where we decoupled the underwriting rules from the application code into a JSON-based DSL so the actuaries and lawyers who actually owned the rules could change them without touching the engineers. It is the same pattern underneath every modern policy-as-code system (OPA, Cloud Custodian), every feature flag service, every Kubernetes admission controller.

What the new wave doesn't change

I get asked whether the agentic-coding tooling I'm shipping at AVEVA — GitHub Copilot CLI, Claude Code, ChatGPT Codex, the Core AI Services platform we're building — invalidates this argument. The phrasing is usually some version of isn't the next framework cycle going to be so fast that nobody can afford to learn patterns anymore?

My honest answer is the opposite. The faster frameworks turn over, the more valuable patterns become, because pattern fluency is what lets you read a generated PR in thirty seconds instead of thirty minutes. When the agent hands me a Rust implementation of a CQRS read-model projection, I don't have to learn what the agent wrote — I have to verify that the pattern is intact, the bounded context is respected, the projection is idempotent, the event ordering is preserved. Those are pattern questions. The agent is fluent in syntax. I am fluent in shape. The collaboration works because we are not competing on the same axis.

The engineers I see struggling with agentic tooling are, almost without exception, the ones who learned frameworks as their primary unit of knowledge. They cannot review what the agent produces, because they have no internal model more abstract than the code itself. The engineers who are thriving learned patterns first; the agent has compressed the framework-shaped part of their job, and the pattern-shaped part — the part that always mattered — is now the entire job.

The closing point

The languages and frameworks turn over every few years. Nine languages in twenty-five years is, statistically, about right; I expect to ship in a tenth before the decade is out. None of that turnover has felt like loss, because none of what I actually invested in went obsolete. The DDD I learned in 2009 is the DDD I used at AVEVA last week. The reconciliation loop I wrote at Philips in Go is the reconciliation loop I'm extending at AVEVA in TypeScript. The event-sourced ledger that we built in C# at Alpari, I later rebuilt in Rust at dunnhumby, and would build again tomorrow in whatever language the team picked.

If you are early in your career and trying to decide what to invest in: invest in patterns. Read Evans, Vernon, Hohpe and Woolf, Fowler. Read everything Greg Young and Udi Dahan have published on CQRS and event sourcing. Sit with the books long enough that the patterns become your default mental shape for new problems, and the next framework becomes a dialect rather than a language.

If you are mid-career and feeling the framework treadmill catching up to you: it is not too late. The compounding starts the day you switch the unit of learning from the tool to the pattern underneath it. That switch is the difference between twenty-five years that feel cumulative and twenty-five years that feel episodic. I know which side I'd rather be on.

Madu