← writing

Why I stopped using ORMs for complex queries

14 Nov 2024

ORMs are genuinely useful for 80% of your queries. The simple stuff — find by ID, insert a row, update a field — they handle well. Where they fall apart is the 20% that actually matters: the complex joins, the window functions, the CTEs, the queries that need to be fast.

The illusion of portability

The pitch is usually: "Write once, run on any database." In practice, I've never once switched databases mid-project. The portability is theoretical. What you actually get is a layer of abstraction over SQL that leaks in unexpected ways.

When you need to express something your ORM doesn't support natively, you end up writing raw SQL anyway — but now it's embedded in a string inside an ORM call, with none of the type safety or autocomplete you were promised.

Where I draw the line

Simple CRUD: ORM. Anything with multiple joins, aggregations, or performance requirements: raw SQL with a typed query builder or just pg directly.

The tools I reach for now:

The actual tradeoff

Raw SQL is more verbose. You write the query, you write the type, you maintain both. That's real overhead.

But the query is readable. You can paste it into your database client and run it. The execution plan is predictable. You're not debugging what the ORM generated at 2am when prod is slow.

For a personal project or a prototype, use whatever. For anything that sees real load, know your SQL.