Prompt file imported from wyzlee/SEO-GEO (
.claude/commands/db-migrate.md). Copyright stays with the author.
/db-migrate
Gestion des migrations Drizzle pour l'app SEO-GEO.
Usage :
/db-migrate generate— génère une migration depuis le diff entreschema.tset la DB/db-migrate apply— applique les migrations pending sur dev (Neon branch dev)/db-migrate apply-prod— applique sur prod (main branch Neon) avec double check/db-migrate status— affiche migrations appliquées vs pending
Prérequis
drizzle.config.tsconfiguré (pointe vers Neon, litDATABASE_URL)DATABASE_URL_DEVetDATABASE_URL(prod) dans.env.local- Neon project avec branching activé
Étape 1 — generate
npm run db:generate
→ Crée drizzle/<timestamp>_<name>.sql.
Review obligatoire :
cat drizzle/<timestamp>_*.sql→ lire le SQL généré ligne par ligne- Chercher :
DROP TABLE→ STOP, window de maintenance + backup Neon branching requisDROP COLUMNsur colonne avec data → STOP, plan en 2 étapes (add_new → backfill → drop_old)ALTER COLUMN ... TYPE ...breaking (ex: text → int) → même règle- Renommages → Drizzle peut les générer incorrectement comme DROP + ADD. Toujours vérifier.
- Si anything destructive détecté → abort, repenser le schema ou préparer migration multi-étapes
Si review OK → commit le fichier SQL avec le change schema.ts :
git add drizzle/ lib/db/schema.ts
git commit -m "db: migrate <description>"
Étape 2 — apply (dev)
Sur branch Neon dev (éphémère ou long-lived dev) :
# .env.local contient DATABASE_URL pointant vers dev branch
npm run db:migrate
Vérifier :
- Aucune erreur dans la sortie
- Les colonnes / tables apparaissent comme attendu :
npm run db:studio(Drizzle Studio) pour inspection visuelle
Étape 3 — tester
- Run l'app :
npm run dev - Tester les flows affectés par la migration
- Si RLS ou triggers ajoutés : vérifier comportement
- Si nouvelle table métier : vérifier insert/select/delete via UI ou curl
Étape 4 — apply-prod
⚠️ Opération sur prod — procédure stricte :
- Vérifier que la migration est committée sur
mainavec review humain (PR mergée) - Backup via Neon branching :
# Créer une branch "pre-migration-<timestamp>" depuis main # Via Neon dashboard OU Neon MCP (create_branch) - Confirmer avec user :
Vous êtes sur le point d'appliquer les migrations SUIVANTES en PRODUCTION : - drizzle/<timestamp1>_*.sql - drizzle/<timestamp2>_*.sql Backup créé : pre-migration-<timestamp> DATABASE_URL prod : ***redacted*** Tapez "APPLIQUER" pour confirmer. - Si confirmation →
DATABASE_URL=<prod> npm run db:migrate - Monitorer :
- Logs app (Sentry / logs structurés) pour 5 min post-migration
- Error rate sur
/api/* - Healthcheck
/api/healthrépond 200
- Si erreurs → rollback via Neon branching (promouvoir la branch
pre-migration-<timestamp>en main)
Étape 5 — status
npm run db:migrate --check # ou commande équivalente drizzle-kit
Affiche :
- Migrations appliquées (lues depuis la table
drizzle_migrations) - Migrations pending (fichiers dans
drizzle/pas encore appliqués) - Divergence éventuelle entre
schema.tset DB (drizzle-kit détecte)
Règles strictes
- Jamais
db:pushen prod (Drizzle push sans migration = dangereux). - Jamais
DROP TABLE/DROP COLUMNsans backup + confirmation explicite. - Toujours review le SQL généré avant apply, même sur dev.
- Toujours committer
drizzle/*.sqlavec le changeschema.ts(atomique). - Séparer schema additifs (safe) et schema breaking (risky) — jamais dans la même migration.
- Jamais manipuler manuellement la table
drizzle_migrations(sauf recovery incident documenté).
Patterns multi-étapes pour migrations breaking
Renommer une colonne
Mauvais (perd les données) : ALTER TABLE ... RENAME COLUMN a TO b
Bon (zero-downtime) :
- Migration 1 :
ADD COLUMN b - App code : dual write (écrire dans
aetb) - Backfill script :
UPDATE t SET b = a WHERE b IS NULL - App code : lire depuis
b, stopper écriturea - Migration 2 :
DROP COLUMN a
Changer le type d'une colonne
Similaire : ADD new column (new type) → backfill → stop writes old → drop old.
Supprimer une table
- Marquer la table deprecated (commentaire schema, tests FAIL si lue)
- Logger chaque lecture résiduelle pendant N jours
- Quand zéro lecture → migration
DROP TABLE
Neon branching tips
- dev branch : long-lived, pour itérations continues
- preview- branch : éphémère, par PR pour tester migrations avant merge
- main branch : prod, protégée
- pre-migration- : backup point-in-time avant apply prod
Commande Neon MCP utile : mcp__Neon__create_branch, mcp__Neon__reset_from_parent.
Edge cases
- Schema conflict (drizzle-kit détecte divergence) → soit apply les migrations pending, soit
db:pushsur DEV pour resync (jamais prod) - Migration partielle (erreur à mi-parcours) → lire drizzle_migrations pour voir lesquelles ont réussi, potentiellement reset depuis backup
- Timeout sur longue migration → fractionner en plusieurs migrations courtes, ou exécuter off-hours
- Conflict concurrent writes pendant migration → prévoir maintenance window si ADD NOT NULL COLUMN avec DEFAULT expensive
