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.
Keep reading
- api designobservability
Why Your API Rate Limiter Is Costing You Real Traffic
Misconfigured rate limits don't just reject spam. They reject paying customers during traffic spikes, silently kill retries, and hide themselves behind generic 429 errors. Here's what actually happens when you measure it.
- inventory managementsupply chain
When Your Inventory Math Doesn't Match What's Actually in the Warehouse: How Rounding Errors and Manual Updates Turn Small Problems Into Supply Chain Disasters
Small inventory errors compound fast. Rounding mistakes, manual data entry delays, and systems that don't talk to each other create cascading supply chain failures that cost more to fix than the system that would prevent them.
- operationscost control
Why Your Vendor Lock-In Isn't Just a Tech Problem—It's Bleeding Your Operating Budget
Vendor lock-in costs most businesses real money every month: wasted time, duplicated effort, and the panic bill when you finally want to leave. Here's what it actually costs, and how to spot it before you're trapped.