What Drizzle's migration system actually generates, why that's better than magic ORM migrations, and how to handle the edge cases.
Drizzle's migration system generates plain SQL files. Not an abstraction layer, not a DSL — actual SQL you can read, audit, and edit.
bun drizzle-kit generate
# Creates: drizzle/0001_add_thoughts_table.sqlOpen that file and you see:
Karanveer Singh Shaktawat
Full Stack Engineer & Infrastructure Architect
Building portfolio, contributing to open source, and seeking remote full-time roles with significant technical ownership.
Pick what you want to hear about — I'll only email when it's worth it.
Did this resonate?
The difference between PgBouncer's pooling modes, why transaction mode breaks prepared statements, and which to use for Next.js apps.
A common Next.js App Router misconception: 'use client' doesn't make a file client-only — it marks the boundary where the client tree starts.
CREATE TABLE IF NOT EXISTS "thoughts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"content" text NOT NULL,
"mood" text,
"tags" text[] DEFAULT '{}' NOT NULL,
"published" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);This is the entire migration. No magic. No hidden state beyond the __drizzle_migrations table that tracks what's been run.
import { migrate } from "drizzle-orm/node-postgres/migrator";
await migrate(db, { migrationsFolder: "./drizzle" });Or via CLI:
bun drizzle-kit migrateDrizzle can't infer intent. If you rename mood to feeling in your schema, drizzle-kit generate sees a deleted column and a new column — and generates a migration that drops and recreates, losing data.
Fix: edit the generated SQL manually before running it.
-- Generated (wrong):
ALTER TABLE "thoughts" DROP COLUMN "mood";
ALTER TABLE "thoughts" ADD COLUMN "feeling" text;
-- Edit to (correct):
ALTER TABLE "thoughts" RENAME COLUMN "mood" TO "feeling";Because migrations are plain SQL files, this edit is trivial. With magic ORM migration systems, you're at the mercy of their rename-detection heuristics.
Drizzle tracks applied migrations in drizzle/meta/_journal.json. Don't edit this manually unless you know exactly what you're doing — it's how the runner knows what's already applied.
If you need to revert a migration in development: delete the SQL file, delete its entry from _journal.json, and run drizzle-kit generate again. In production: write a new down-migration SQL file and apply it manually.