Decision guide

ORM vs Query Builder vs Raw SQL

How should application code talk to a relational database: a full ORM, a query builder, or SQL with code generation?

Side by side

CriterionFull ORMQuery builderRaw SQL with code generation
Productivity for CRUDHighestMediumLower (unless generated)
Control over SQLLowest (escape hatches available)HighFull
Type safetyModel-levelHigh in typed buildersHigh with code generation
Common failure modeN+1 queries and over-fetchingRe-implementing ORM featuresString-built SQL injection without parameters
Complex queriesAwkward; drop to raw SQLGoodBest
MigrationsUsually built inSeparate toolSeparate tool (Flyway, Atlas, golang-migrate)

When to choose each

Full ORM

Maps tables to classes with relationships, change tracking and migrations (Django ORM, SQLAlchemy ORM, EF Core, Hibernate, Eloquent, Prisma).

Choose when

  • CRUD-heavy business apps with many entities
  • Teams that want migrations, relations and validation integrated
  • Rapid development with conventions

Avoid when

  • Complex reporting queries and heavy SQL tuning
  • When hidden queries (lazy loading, N+1) would hurt and nobody watches query counts

Query builder

Composable, often type-safe SQL in the host language without object mapping (Kysely, Drizzle, Knex, jOOQ, SQLAlchemy Core).

Choose when

  • Dynamic queries (filters, sorting) built in code
  • Type safety with SQL-like control
  • Teams comfortable with SQL who dislike ORM magic

Avoid when

  • You want identity maps, change tracking and relationship loading done for you

Raw SQL with code generation

SQL written in .sql files or strings, with tools generating typed functions from the queries (sqlc, PgTyped, SQLDelight) or a thin driver.

Choose when

  • Performance-critical paths and complex queries (CTEs, window functions)
  • Teams with strong SQL skills
  • Go services and data-heavy backends

Avoid when

  • Lots of dynamic query composition
  • Teams that would end up hand-writing CRUD for dozens of tables

Verdict

Use the ORM that comes with your framework for typical business apps, and learn to see the SQL it generates (query logs, N+1 checks). Use a query builder when queries are dynamic and you want type safety without object mapping. Use SQL with code generation for performance-critical or SQL-heavy services. Mixing is normal: an ORM for CRUD plus hand-written SQL for reports, always with parameters, never string concatenation.

Learn more

Sources

Was this page helpful?