Breaking Free From the ORM: Why Move On?

Time to escape object relational mapping

Omar Rayward
Building the system
5 min readAug 5, 2018

--

TL;DR, Reduce complexity in in your programs by avoiding the use of ORMs.

Object Relational Mapping or ORM is one of those constructs that, as software developers, we would have benefited from if it simply never existed.

Three years ago, frustrated by the number of times I had to revisit Django’s ORM documentation, Myles Byrne suggested I read The Vietnam of Computer Science. It was a refreshing experience that articulated my distaste for ORMs.

The Vietnam of Computer Science is from 2006, and it is still very relevant today. Following the Lindy effect, it will probably remain relevant ten years from now. If you have ever exploded in anger because of some unexpected consequence using an ORM, I would recommend setting some time aside over the next week to read it and understand the structural limitations of ORMs.

Four Good Reasons to Escape ORMs

1. ORMs GREATLY INCREASE INCIDENTAL COMPLEXITY (EASY IS NOT SIMPLE)

ORMs are “easy” to use, but they come with a lot of incidental complexity that sooner or later will surface in our programs. In this case, easy is not the same as simple.

Incidental complexity in Rich Hickey’s words:

The output of the constructs that we create in our programs is a source of complexity. We’re going to call that incidental complexity. Incidental complexity wasn’t part of what the user asked us to do. We chose a tool. It had some inherent complexity in it. It’s incidental to the problem. I didn’t put the definition in here, but incidental is Latin for your fault.

email = User.find(id=1).email

OK, that was easy. I just needed to get theUser object by id then retrieve its email.

Now, try answering these questions:

  • Was a database connection open when executing that line of code or was it open at a previous time?
  • Are we using a connection from a database connection pool?
  • How much data did we retrieve from the database?
  • Do we need all the data retrieved from the database?
  • Is that line of code fetching data from the database or is it accessing an internal cache?
  • If there is a cache, when is it going to be refreshed?
  • Does that line of code automatically commit to the database?

From reading that single line of code, I am unable to answer any of those questions.

Now, let’s try answering the previous questions with this code:

conn = psycopg2.connect('postrges://...')
with conn.cursor() as cur:
conn.execute('SELECT email FROM users WHERE id=1')
email = conn.fetchall()
conn.commit()

This snippet of code is more challenging to create because it requires understanding what we are doing. But, it is a lot simpler than the previous “one-liner”.

From the new code, we can answer all of the questions.

Here’s another example:

team_names = User.find(id=1).teams.map(team => team.name)

Try answering this question:

  • How many database queries is that “one-liner” making?

2. ORMs ALWAYS BREAK THEIR IMPLICIT CONTRACT

Each time we introduce a dependency, we concede to the following tacit agreement:

Hey developer, if you use me as a third-party library, I’ll make your life easier:

- I’ll abstract a piece of knowledge you don’t need to build a better product faster.

- I’ll provide such good API and documentation that you will rarely (if ever) need to understand how I’m doing what I’m doing. Trust me, I have you covered.

The implicit value proposition of an ORM is it will abstract anything related to data using a familiar Object Oriented Interface.

This is simply not true.

ORMs are not a substitute for databases. If and when we need to use the full capabilities of the underlying database, the ORM falls short, resulting in the need to write raw database queries.

When using an ORM, it’s not enough to know how to use a database feature. We also need to know what the best way to implement it is using its DSL (Domain-specific Language). The back and forth between the database DSL and the ORM’ DSL is useless overhead.

When there is doubt, there is no doubt.

If you are currently using an ORM and can’t escape it; whenever debating whether to write raw SQL or learn the ORM’s DSL, then you’ve gone too far — always use raw SQL.

3. ORMs IMPLICITLY ASSIGN MORE VALUE TO CODE THAN DATA

An ORM is a piece of software that purposefully hides the underlying database, while simultaneously aligning itself with an architecture that assigns more value to code than data.

By using an ORM, we silently scream, “The source of truth is the code — the database is just an artifact!”

This is wrong. Very wrong. Between code and data, data is the asset; code is the liability. Code is just a tool to expose data to different stakeholders. We should make architecture decisions aligned with this view of the world.

4. ORMs COMBINE TOO MANY COMPETENCIES

When we’re sold on an ORM, we are usually buying three constructs at once:

  • Database migrations
  • Database connection handling with predefined or configurable connection pooling
  • Database read/write queries with a predefined or configurable commit logic

We usually place these three constructs in the same location as the business logic (application code), which is especially painful to see for database migrations.

By placing migrations within the application code we bundle the database schema lifecycle with the application’s lifecycle. It’s noticeably worse if we want to create other applications on top of the same database (as we won’t be able to decouple the new application’s lifecycle from that holding the database migrations).

Building the system

Complexity in our code is a liability. As developers, we add a great degree of complexity to our code by using ORMs.

Via negativa (less is more): By avoiding the use of ORMs and understanding what functionality we need to implement in our applications, we can build a robust foundation for our engineering team. It opens deeper level architecture discussions such as whether to favor objects versus data, leading to a stronger overall system.

In this first of a four-part series, we have explored why we need to escape the use of ORMs. In the next three parts, we will explore tools and provide practical advice on how to remove the ORM by migrating the database with different tools, handling database connections ourselves and managing database queries.

References

  1. The Vietnam of Computer Science(Ted Neward).
  2. Simple Made Easy (Rich Hickey).The transcript.

--

--