database · tutorial · comparison

Prisma vs Raw SQL: The Honest Solo Math

ORM magic versus readable queries: migration pain, debugging transparency, and the lock-in nobody prices. My verdict after both. Inside.

Lithograph poster of the magician Kellar levitating a woman, 1894
Lithograph poster of the magician Kellar levitating a woman, 1894 · Photo: Strobridge Lithographing Co., public domain, via Wikimedia Commons

The ORM migration that ate lunch

The classic horror story. A Friday-afternoon migration where a renamed field is generated as a drop plus an add.

Restoring from backup takes lunch.

No hate for ORMs here.

Distrust magic that nobody can read at 2 a.m.

Generated SQL, unreviewed.

Migration diffs, skimmed.

Query behavior hidden behind method chains.

Solo founders debug alone. Transparency is not aesthetic. It is survival gear.

The real choice is magic or readable.

ORM or raw SQL at solo scale

Axis Prisma-style ORM Raw SQL with shared actions
First query Minutes, with autocomplete An hour, if SQL is new to you
Migration safety Generated, so review or regret Written by hand, intent explicit
Debugging at 2 a.m. Generated-SQL archaeology Read the query file
Lock-in A schema dialect and a client SQL ports anywhere
Agent compatibility Plausible-looking magic Readable SQL
Hidden N+1 Buried in includes Visible in the query text

Three lessons come out of that migration.

Generated migrations encode guesses. Rename detection, nullability inference, default synthesis. Each is a guess presented as a plan. Review converts guesses to intentions. Unreviewed generated changes are fiction with formatting.

Method chains hide N+1. An include nest looks clean while emitting dozens of queries. Raw SQL shows its cost on its face.

Agents write better SQL than fluent calls. Models trained on oceans of SQL write readable queries. Fluent ORM calls come out plausible but off. Readable output reviews fast. Magic output reviews never.

What to do

Write SQL you can read at 2 a.m.

Every query a complete sentence.

Tables named.

Filters explicit.

Limits present.

Cost countable.

New engineers, human or silicon, read the file and know the database's behavior. No generated layer to compile in your head.

It is verbose on purpose. Explicitness is documentation that executes.

Wrap queries in the same actions on every engine.

The app asks for "this user's orders" and never sees SQL or a driver. Actions abstract the engine without abstracting the meaning, which is where an ORM abstracts both.

Switching engines means reimplementing the actions. Mechanical. Reviewable.

Change the database by hand, and review like money depends on it.

Migrations are written by a person. Additive only. With defaults.

A reviewer asks four questions.

Is it additive?

Are the defaults safe?

Is a backfill needed?

Can it roll back by ignoring the column?

A Friday drop-plus-add dies structurally, because no generator invents destruction.

The honest comparison

Question ORM Raw SQL with actions
Types A generated client Written beside the queries
Switching engines Tied to the client's dialect Reimplement the actions
New developers Learn the ORM first Read the SQL files
Day one Fast Slower

Raw SQL is slower on day one.

You lose autocomplete magic.

Generated types need extra setup.

And it demands SQL literacy.

SQL is learned once and used everywhere, while client libraries churn.

BoringPush claims no measured incident rate from either approach. The argument is about readability, not a benchmark.

Know this first

Raw SQL is slower on day one. You lose autocomplete magic. You need to know SQL.

For teams with juniors, an ORM is a fair choice.

Why BoringPush is the way to do this

Queries you can read.

BoringPush uses plain SQL and adds no ORM. One set of actions sits on top. Database changes are written by hand and additive.

Nothing hidden at 2 a.m.

FAQ

Is this even close for teams with juniors?

Closer. Juniors ship faster with autocomplete guardrails, and teams spread the cost of learning the ORM across several heads.

A solo developer holds all context in one head, so readability beats assistance. For teams, an ORM with strict review rituals, where generated migrations are never merged unread, is a fair choice.

What about Drizzle?

It is a genuine middle ground: SQL-shaped, readable, with type safety and migration tooling. If raw feels austere and Prisma feels magical, Drizzle splits the difference.

Our ranking for solos is raw first, Drizzle second and Prisma third. BoringPush itself uses raw SQL and adds no ORM dependency.

How do types work without a generated client?

Write types beside the queries and validate inputs at the boundary. There is a little duplication, since types and the table definition describe the same shape twice. Both copies are visible and human-readable, which beats magic with one side hidden.

Do raw queries slow down agents?

The opposite. Agents write readable SQL well and often make plausible-but-off fluent calls in ORMs.

Raw queries review fast: read the SQL and check the logic. Readable output plus tireless generation is a strong combination.

When is an ORM genuinely the right call?

Prototypes with a disposal date, junior-heavy teams, and projects already invested in the ORM's ecosystem. ORMs are not villains. They are leverage with an interest rate, so borrow deliberately and repay in review rigor.

How do I move off an ORM without stopping the world?

By attrition. Write new queries raw, port old ones when you touch them, and shrink the generated client each quarter.

Delete it when the last import dies. It takes months, and each ported query gets reviewed.

Keep reading

+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)

The Agent-First Shipping Kit

Command from your phone.

Built so autonomous AI agents build what you want without breaking what's already paying you.

$199

Get BoringPush

One-time payment · Lifetime updates · Unlimited projects.

Keep reading