HiveCore Dev logo hivecore.dev

Why I Stopped Using ORMs (and When I Still Do)

// essay · HiveCore Dev · 2026-05-09

TL;DR: ORMs solve a problem most apps don't have. Plain SQL with a thin query builder beats Prisma/SQLAlchemy/Sequelize for most production workloads. But there's a real case where ORMs win.

The case against

ORMs hide SQL behind an API. That's fine until you need to optimize a query — and then you're translating ORM-speak back into SQL anyway, plus fighting the abstraction.

The N+1 problem is built into every ORM. Lazy loading is an antipattern that ORMs make ergonomic. user.posts.first.comments.first.author.name is six queries dressed as one expression.

What I use instead

Plain SQL with a parameterized query builder. In Python, that's asyncpg + tagged-template SQL or SQLAlchemy core (not the ORM). In TypeScript, it's Kysely or Drizzle.

// kysely — query builder, no ORM
const users = await db
  .selectFrom("users")
  .where("active", "=", true)
  .select(["id", "email", "created_at"])
  .orderBy("created_at", "desc")
  .limit(10)
  .execute();
// users is fully typed as { id: number; email: string; created_at: Date }[]

When ORMs still win

Admin panels. Internal tools. CRUD-heavy apps with no perf-critical paths.

Schema migrations. Even when I write SQL by hand for queries, I let the ORM (or its migration tool) generate migration files.

Auto-generated APIs. Hasura, PostgREST, Prisma+tRPC — all of these need an ORM-style metadata layer.

The hidden cost of leaving

Every team I've moved off an ORM has had a 2-week period where everyone re-learns SQL. That cost is real. Don't migrate unless you have at least one person on the team who already writes SQL fluently.

A middle path

Use the ORM's query builder, not its model layer. SQLAlchemy's core API is a query builder; SQLAlchemy's ORM is a model layer with sessions, relationships, and lazy loading. The first is great. The second is what most 'I hate ORMs' rants are actually about.

Related reading