Skip to content

Database Development Patterns

A reporting query that scanned a few thousand rows in staging now sequentially scans 40 million in production and times out the dashboard. A “quick” column rename ships a migration that takes an ACCESS EXCLUSIVE lock and freezes every write for ninety seconds. And an ORM relation you added last week quietly fires one query per row, so the orders page makes 500 round-trips to render. These are the database failures that don’t show up until real data and real concurrency arrive — and they’re where a coding agent is genuinely useful, because the fix is a recognizable pattern, not invention.

This recipe shows the prompts that get the AI to read an actual EXPLAIN ANALYZE plan, propose a safe expand/contract migration, and collapse an N+1 into one batched query. The anchor stack is PostgreSQL with Drizzle ORM, but the prompts work just as well with Prisma or raw SQL.

  • A prompt that pastes a real EXPLAIN (ANALYZE, BUFFERS) plan and gets back targeted index recommendations, not generic advice.
  • A zero-downtime column-rename prompt using the expand/contract pattern, so writes never block.
  • A prompt that turns an N+1 ORM access pattern into a single batched query.
  • The three-tool workflow for schema work (Cursor, Claude Code, Codex) and how the Postgres MCP server changes it.
  • A “When This Breaks” checklist for the bad advice AI models give on database work.

Schema generation is the one task where all three tools do roughly the same thing — read your existing tables, match the conventions, propose new ones. The difference is the surface you drive it from.

Open your existing schema file (src/db/schema.ts) so Agent mode has it in context, then Cmd/Ctrl + I:

Following the Drizzle conventions in @src/db/schema.ts, add orders and order_items tables. Include created_at/updated_at with defaults, a deleted_at for soft delete, a foreign key from order_items to orders with ON DELETE CASCADE, and an index on orders(user_id, created_at DESC) for our most common lookup.

Review the diff before accepting, and use a Checkpoint so you can revert the whole schema change if the index choice is wrong.

Optimizing a slow query from its real plan

Section titled “Optimizing a slow query from its real plan”

The single biggest upgrade to AI query tuning is refusing to let it guess. A model handed only the SQL will pattern-match (“add an index on the WHERE columns”) and is often wrong. A model handed the actual execution plan can see the sequential scan, the row-count misestimate, and the spilled sort — and recommend the index that matches.

The discipline that makes this reliable: ask for CREATE INDEX CONCURRENTLY explicitly (a plain CREATE INDEX locks writes on a large table), and demand justification against the plan. If the AI recommends four indexes “to be safe,” push back — every index is a write-time tax, and the model has no idea about your write volume unless you tell it.

Zero-downtime migrations: the expand/contract pattern

Section titled “Zero-downtime migrations: the expand/contract pattern”

Renaming a column with ALTER TABLE ... RENAME COLUMN looks harmless and is fine on small tables, but on a hot table the lock plus any dependent view rebuild can stall production. The safe pattern is expand/contract: add the new column, backfill, dual-write from the app, switch reads, then drop the old column in a later deploy. The AI knows this pattern — your job is to make it produce all the steps, not just the rename.

Watch for the model collapsing this into a single RENAME “because it’s simpler.” It is simpler, and it’s the thing that pages you at 2am. The batched backfill is the part most often missing — without it, one big UPDATE rewrites every row under a lock and you’ve reintroduced the downtime you were avoiding.

Killing an N+1 with a single batched query

Section titled “Killing an N+1 with a single batched query”

ORMs make N+1 effortless to write: iterate a list, touch a relation, and you’ve fired one query per element. The fix is to load the children in one query keyed by parent ID. Give the AI the offending code and the model, and it will reshape the access pattern.

The verification step — “show the two SQL statements” — matters because some AI rewrites swap an N+1 for a single query with a Cartesian join, which loads the same row data N times over the wire. Two clean statements grouped in memory is the shape you want.

  • API patterns — idempotent endpoints, cursor pagination, and OpenAPI generation on top of this schema.
  • Serverless patterns for running these databases in cloud and edge environments.