Skip to content

Postgres + Drizzle, after a year

Not a tutorial. Lessons from a year of running Drizzle in production across JomJual, Lunara, and a few side builds.

· 4 min read

A year ago I migrated a side project from Prisma to Drizzle over one long Saturday. I have not gone back. This is not a Drizzle vs Prisma post. This is what Drizzle looks like once the honeymoon is over and you are actually running the thing.

What I love

The schema is just TypeScript. That sounds like nothing until you have gone through a Prisma migration where the generator hangs on a Windows filesystem and you cannot explain to your cofounder why a no-code schema change is blocking deployment.

pgTable('orders', { ... }) is ugly. It is also a function call. I can loop over it. I can factor shared columns into a helper. I can write a test that asserts every tenant-scoped table has a store_id foreign key, and the test runs before any migration goes near Neon. Try doing that with a .prisma file.

What I had to unlearn

Prisma's relation helpers hide N+1 under a friendly API. Drizzle does not. with is a real join, and you have to think about it. The first time I shipped a product listing that did 31 round-trips for "select products with variants" I learned this the hard way, at 22:00, staring at Neon query logs.

The fix was reading the docs — Drizzle's with generates a single query with lateral joins when you configure relations properly. But the default when you forget is not magic. It is slow. You get what you asked for.

Migrations

drizzle-kit generate is fast and deterministic. drizzle-kit push is fine for early development, and dangerous after your first user. Once the database has real data, every migration goes through generate → review the SQL → run in a transaction.

I still use push for local iteration. I do not use it in CI. There is no world in which an unreviewed ALTER TABLE hits production.

The actual ergonomics win

Type-safe query builders are a meme category now, but the one that changed my life is the query response type. When I select five fields from three joined tables with a filter, the returned row is typed exactly for those five fields. No any, no partial, no as unknown as SomeType. You change the select, the consumer breaks at compile time, you fix it, you move on. This is the actual reason to use Drizzle.

Would I pick it again

Yes. But I would also not judge anyone who picks Prisma. The right answer for most people is "the one your team already knows." Pick something and ship. Database tools are a worse rabbit hole than editors.

Written by

Faiz Kasman

Software engineer in Kuala Lumpur. Payments, multi-tenant SaaS, and inventory infrastructure. Currently building the Shell Malaysia ParkEasy app.

Keep reading