Skip to content

The False Promise of ORMs

Published:

ORMs suck. They suck because they offer a promise to the developer that they cannot fulfill beyond a certain low level of complexity. ORMs promise that the developer does not have to learn SQL and does not have to learn the database, or even know what the database is. Some ORMs like Hibernate go a step further and promise that a developer can completely abstract themselves from the data system to the point where they could swap out one data system for another by simply flipping a switch in an IOC config. As you’ll see below, for anything above a bootcamp-level project, ORMs cannot fulfill this promise.

Why do these tools exist?

The tool builders must look at the different data systems offering a SQL interface and think “ah, we can write a single tool to abstract all data systems”. This is a powerful temptation that is too much to resist for a certain type of Uncle Bob developer who seeks to solve abstraction puzzles by wrapping everything in ports and adapters.

If that’s who’s building these tools, who’s using them? Obviously, the same type of onion-pattern-brained architect, but especially enterprises, who reach for ORMs because they consider them a talent arbitrage. The gambit is that you can spin up junior engineers without SQL experience and quickly get them to work on a fullstack application. I’ve seen this work to some extent, but the gambit fails at scale when performance becomes the bottleneck.

Breaking the glass breaks the promise

The false promise is revealed when:

The database is bound to leak through the ORM into the app when you break the glass to use the database directly. Inevitably, the tool builders must elevate the underlying data systems through the use of ‘custom SQL’ or db-specific syntax. You might as well accept that you chose a database for a reason, and spend some time learning that system and SQL.

When is an ORM good? When it’s a type-safe query builder

Is it possible to achieve the promise of an ORM? Not fully, but lighter-weight ORMs and query builders can help solve some of the pain points of the alternative (SQL strings and hand-built rowmappers):

These tools work best when they provide some type safety to rowmapping and updates and give some quality-of-life improvements to the boilerplate that otherwise arises trying to manage string SQL statements and DAOs.

There’s no way to avoid knowing the tools

App developers might be lucky to work with a DBAs in certain companies who can provide the buffer, but this is a rare situation in my experience. Relying on ORMs means building a three-fold more complicated system. Instead of just learning databases and SQL, the app developer now has to learn the ORM’s DSL (domain-specific language), databases and SQL, and the unique emergent issues that arise in the interactions between the ORM and the database.

Ultimately, ORMs promise that you don’t need to know the database to build an application. Why then did you choose one database over another, or any database at all? Databases are truly one of the most powerful systems available to an application developer and ignoring the specific features is a waste. Perhaps ORMs made sense when there were three major RDMBs vendors and the choice came down to “which vendor bought the VP the most expensive dinner”. With the vast proliferation of specialized data systems available today, the choice of data system is one of the first choices to make, and probably the most important. Lean into this choice and leverage the powerful features available.

I’ll leave you with this function I found in a codebase I used to maintain which uses a Scala full-stack framework with an ORM. It returns void and is chained to the ORM’s ‘break glass’ SQL statement function. There are 4,260 references to this function which perfectly encapsulates the failed promise of ORMs:

IHaveValidatedThisSQL("programmerName", "2021-07-30")


Next Post
Thoughts on my semi-annual blog rewrite