docs(db): record the migrations decision (#219)

node-pg-migrate keeps the schema; Drizzle is for queries only. The conventions doc said this was unsettled and now says what was settled and why: drizzle-kit cannot diff expression indexes and emitted six statements for one column, our migrations are mostly prose that generated SQL does not carry, and data migrations cannot be generated at all.

The reasoning in full is on the issue. This is the version a reader converting a query will actually find.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 09:33:09 -05:00
co-authored by Claude Opus 5
parent 7ef3553744
commit 508eac715b
+6 -2
View File
@@ -69,6 +69,10 @@ Column names differ, and the difference is load-bearing. The mirror is camelCase
`db` and `pool` are exported from `src/db.ts` and share one pool, deliberately. Conversion is file by file across 187 sites, so most queries will be raw `pg` for a long time and the two must not open separate connection pools — a transaction on one would be invisible to the other, and the configured limits would silently double.
## Not settled
## Migrations stay hand-written
Whether generated migrations replace `node-pg-migrate` is **#219**, and nothing here depends on it. The first generated migration after a pull also emitted drops and recreations of the three expression indexes, which needs hand-editing and takes real locks on a large table; and data migrations cannot be generated at all. Do not start generating migrations as a side effect of converting a query.
Decided in **#219**: `node-pg-migrate` keeps the schema, Drizzle is for queries only. Do not start generating migrations as a side effect of converting a query.
Three reasons, all measured rather than assumed. `drizzle-kit generate` cannot diff expression indexes, so adding one nullable column emitted six statements — three `DROP INDEX` and three `CREATE UNIQUE INDEX` alongside the `ALTER` — and those rebuilds take real locks on a large table. Our migrations are mostly prose, and generated SQL carries none of it: a rule that every generated migration is annotated before merge is a rule that holds for three migrations and then quietly stops, with the failure invisible because the migration still works. And data migrations cannot be generated at all, so anything touching existing rows stays hand-written regardless.
The workflow: write the migration by hand, then run `drizzle-kit pull` to refresh the mirror. `drizzleSchema.integration.test.ts` fails if you forget.