Maybe the database got it right

programming
Back

Maybe The Database Got It Right

When I started writing software, I was taught almost without discussion, that the database was something to keep at a distance.

Not ignored. Not disrespected. Just… kept out of the way.

You modeled your system in memory. You expressed your ideas in objects. You passed those objects through layers. And somewhere, at the very bottom, a database persisted them. Preferably without influencing anything upstream.

This wasn’t framed as ideology. It was framed as maturity.

“The database is just an implementation detail.” “Don’t let persistence leak into your domain model.” “Think in objects, not tables.”

These were not controversial opinions. They were table stakes.

And yet, over time, I began to notice something strange: despite all this careful distancing, the database kept determining the shape of the system—just indirectly, and often more awkwardly.

When objects ate the world

To understand why this mindset took hold, it helps to remember how dominant object-oriented programming became in the 1990s.

By the late ’90s, Java, C++, and later C# weren’t just languages; they were worldviews. Software was something you modeled. You identified nouns, gave them behavior, and let them collaborate in memory. State lived in objects. Identity lived in references.

The books that shaped a generation reflected this emphasis:

Persistence, when it appeared at all, was treated as a technical nuisance. Something to adapt to the model, not the central focus of the system.

When Domain-Driven Design arrived in 2003, it crystalized this thinking. The “domain model” lived in memory. The database existed to serve it. Any influence flowing the other way was considered a smell.

ORMS flourished in this environment:

Their promise was not just convenience, but protection: from SQL injection, from leaky abstractions, from thinking too hard about joins.

The database, in effect, was demoted. Necessary, but slightly embarrassing. Like inline assembly in a high-level language.

Architecture follows the same instinct

At the architectural level, a similar impulse played out.

In 2000, Roy Fielding formalized REST. Around the same time, Service-Oriented Architecture gained traction. Later, microservices inherited the same core idea: isolate concerns, minimize shared state, draw clear boundaries.

The database, especially a shared one, was increasingly seen as an obstacle to autonomy.

Then came the NoSQL movement. Google’s Bigtable paper (2006) and Amazon’s DynamoDB reframed data storage around scale and availability. Systems like Cassandra and MongoDB made schema flexibility a virtue.

Joins were expensive. Schemas were constraining. Relational thinking was framed as legacy thinking.

And yet, the applications most teams were building did not fundamentally change. They were still systems of record. Still CRUD-heavy. Still deeply relational in nature.

What changed was not the problem, but how far away from it we stood.

When the web grows up

The tension became increasingly visible once the web stopped being simple.

In the early 2010s, frontend development changed character. With the rise of SPAs and later React, the browser became a real application runtime. Screens became stateful. Interactions became rich. Latency became obvious.

At the same time, the classic three-layer architecture hardened: frontend, backend API, database.

But modern frontends didn’t talk to one backend anymore. They talked to many. Authentication services. Billing systems. Search APIs. Internal tools. Third-party providers.

Each API was shaped around its own concerns. Each exposed limited filtering, fixed payloads, bespoke pagination rules.

So frontend teams adapted.

They overfetched and trimmed locally. They re-implemented joins... in JavaScript. They issued waterfalls of requests. They built aggregation endpoints to compensate.

What they needed, increasingly, was not “an endpoint”, but a way to ask questions of data.

And the irony was hard to miss: the database already knew how to answer those questions.

Rebuilding the same machinery elsewhere

When GraphQL appeared in 2015, it felt less like a revolution and more like a recognition. Clients needed expressive queries. They needed to describe what they wanted, not just where to fetch it from.

But GraphQL also meant reintroducing familiar machinery in a new place:

All while the relational database continued to do these things—quietly, competently, out of sight.

It began to feel like we were avoiding a tool not because it was inadequate, but because it didn’t fit the story we had been telling ourselves.

The database keeps adapting

Through all of this, relational databases kept evolving.

They added JSON support. Window functions. CTEs. Row-level security. Logical replication. Sophisticated optimizers that improved without touching application code.

They absorbed ideas from the very systems meant to replace them.

The technology changed. Our assumptions often didn’t.

Maybe databases got it right

There’s a line often attributed to Marshall McLuhan: “We shape our tools, and thereafter our tools shape us.”

For a long time, we shaped our tools around in-memory models, object graphs, and service boundaries. The database was told to stay in its place.

Now, as systems grow more data-heavy, more interconnected, and more latency-sensitive, it may be worth asking whether some of the practices we treat as timeless were really just reactions to a specific moment in history.

Not whether abstraction is bad. Not whether ORMs or microservices were mistakes. But whether reflexively hiding the database still serves us as well as we assume.

We keep rebuilding its capabilities elsewhere. We keep narrowing its role, then compensating for what we removed.

So perhaps the right move now is not to reject those practices outright—but to question them.

And to consider, carefully and without nostalgia, whether in all this effort to work around databases, we overlooked something important.

Maybe—just maybe—databases got it right.

If you found this article useful, consider following me on twitter. I write about once a month about software engineering practices and programming in general.

I'm also developing SynthQL. A type-safe HTTP client for PostgreSQL and I would love to get your feedback.


Other posts you may like...

© Fernando Hurtado Cardenas.RSS