Use Cases (Mis à jour: 02/06/2026)

Migrations de base de données avec Claude Code en production

Guide pratique pour utiliser Claude Code sur les migrations DB : expand/contract, Prisma, CI, backfill et rollback.

Migrations de base de données avec Claude Code en production

Une migration de base de données en production ne doit pas commencer par un simple prompt du type “modifie le schéma”. Le risque principal n’est pas seulement la syntaxe SQL. Il vient de l’ordre des opérations : déploiement applicatif, verrous de base de données, sauvegarde, backfill, feature flags, contrôles CI et stratégie de rollback.

Claude Code devient utile quand on l’utilise comme relecteur et assistant de mise en œuvre. Il devient risqué si on lui demande de générer une grosse migration puis qu’on l’applique sans découpage. Ce guide vise les équipes qui utilisent PostgreSQL et Prisma Migrate, mais le même raisonnement fonctionne avec des migrations SQL écrites à la main.

Gardez les sources officielles ouvertes pendant la review : documentation Claude Code, PostgreSQL sur explicit locking et ALTER TABLE, Prisma sur development and production et CLI reference, ainsi que GitHub Actions workflow syntax.

Le modèle expand/contract

Le modèle de base est expand/contract. Expand veut dire élargir la base pour que l’ancienne version et la nouvelle version de l’application puissent fonctionner en même temps. Contract veut dire retirer l’ancien chemin seulement après validation : ancienne colonne, ancienne lecture, ancienne contrainte ou ancien index.

flowchart LR
  A["Backup and review"]
  B["Expand: add nullable column or new table"]
  C["Deploy code with dual write or feature flag"]
  D["Backfill data in small batches"]
  E["Validate staging and production metrics"]
  F["Contract: add NOT NULL, remove old path"]
  A --> B --> C --> D --> E --> F

L’échec classique consiste à laisser Claude Code ajouter une colonne, copier les données, poser NOT NULL et supprimer l’ancienne colonne dans une seule migration. Cela peut marcher sur une petite base locale, puis bloquer une table users ou orders de plusieurs millions de lignes en production.

Quelques termes sont importants. Un verrou est le mécanisme par lequel la base évite des opérations concurrentes incompatibles. Un backfill est un traitement qui remplit de nouvelles colonnes pour les lignes déjà existantes. Une shadow database est une base temporaire que Prisma utilise en développement pour rejouer l’historique et détecter le drift. Ce n’est pas une protection magique en production.

Commencer par une review

Le premier prompt doit demander une review, pas une modification de fichiers. Donnez à Claude Code la taille des tables, le mode de déploiement, l’ORM et les attentes de rollback.

Review this database migration plan before editing files.

Context:
- Production database: PostgreSQL
- ORM: Prisma Migrate
- Hot tables: users has about 8 million rows, orders has about 25 million rows
- Deploy style: blue/green app deploy, database migration runs in CI/CD
- Requirement: split users.name into users.full_name and users.display_name

Check:
1. Can old and new app versions run at the same time?
2. Which SQL statements may take strong locks or scan the whole table?
3. Which steps must be expand, backfill, validate, and contract?
4. What backup or point-in-time recovery check is needed before deploy?
5. What can be rolled back by app deploy, and what can only be rolled forward?

Return a migration plan first. Do not edit files yet.

La dernière phrase est essentielle. Claude Code peut modifier vite, mais une migration de base de données exige une pause de conception. Si le plan mélange des étapes dangereuses, demandez une version plus prudente.

Rewrite the plan so that no step drops a column, rewrites a large table, or sets NOT NULL before the backfill is verified. Include a staging rehearsal and a production abort condition.

Avec ce cadrage, Claude Code agit comme un reviewer de migration. L’humain garde le jugement de risque, et l’agent aide à lister les fichiers, les commandes, le SQL, les checks CI et les signaux de monitoring.

SQL pour expand

Prenons le cas où users.name doit être remplacé par full_name et display_name. La migration expand ajoute uniquement des colonnes nullable et un index. Elle ne fait pas de backfill, ne pose pas NOT NULL et ne supprime pas users.name.

-- 20260602090000_expand_users_names.sql
-- Keep this migration small. Do not backfill and do not drop users.name here.

ALTER TABLE users
  ADD COLUMN full_name text,
  ADD COLUMN display_name text;

-- Run outside a transaction in PostgreSQL migration tools that support it.
-- CREATE INDEX CONCURRENTLY cannot run inside a transaction block.
CREATE INDEX CONCURRENTLY IF NOT EXISTS users_display_name_idx
  ON users (display_name);

La documentation PostgreSQL explique que ALTER TABLE peut prendre différents niveaux de verrou selon le sous-ordre. S’il n’existe pas de mention d’un verrou plus léger, il faut relire de façon conservatrice. Ne demandez pas à Claude Code de deviner : demandez-lui de rattacher son analyse aux docs officielles.

Avec Prisma, créez la migration sans l’appliquer, puis relisez le SQL.

npx prisma migrate dev --name expand-users-names --create-only
npx prisma validate

Le guide Prisma indique que npx prisma migrate deploy est le chemin pour les environnements de test et de production. Il précise aussi que migrate deploy applique les migrations en attente, mais ne détecte pas le drift et n’utilise pas de shadow database. Un deploy réussi n’est donc pas une répétition complète de production.

npx prisma migrate deploy

Adapter l’application

Après expand, l’application doit accepter les deux schémas. La lecture a besoin d’un fallback, et l’écriture doit remplir les anciens et les nouveaux champs pendant la transition.

// src/domain/userName.ts
type UserNameRow = {
  name: string | null;
  fullName: string | null;
  displayName: string | null;
};

export function readDisplayName(user: UserNameRow): string {
  return user.displayName ?? user.fullName ?? user.name ?? "Unknown user";
}

export function buildNameUpdate(input: { name: string }) {
  const normalized = input.name.trim().replace(/\s+/g, " ");

  return {
    name: normalized,
    fullName: normalized,
    displayName: normalized.length > 40 ? `${normalized.slice(0, 39)}...` : normalized,
  };
}

Une feature flag sépare le changement de structure de base du comportement visible par l’utilisateur. Gardez la nouvelle lecture désactivée pendant le backfill, activez-la après validation, puis désactivez-la si les métriques se dégradent. Pour la mise en œuvre, lisez aussi le guide interne feature flags avec Claude Code.

Ce modèle couvre plusieurs cas concrets : renommer ou découper des champs de profil, ajouter des colonnes calculées comme un solde de facture ou un libellé de recherche, et ajouter des index ou foreign keys sur des tables très utilisées. Dans chaque cas, le découpage réduit le risque.

Backfill par petits lots

Un énorme UPDATE peut augmenter la durée des verrous, le volume WAL, le retard de réplication et le risque opérationnel. Demandez plutôt à Claude Code un script de backfill relançable.

// scripts/backfill-user-names.mjs
import pg from "pg";

const { Client } = pg;
const batchSize = Number(process.env.BATCH_SIZE ?? 1000);
const sleepMs = Number(process.env.SLEEP_MS ?? 200);

const client = new Client({ connectionString: process.env.DATABASE_URL });

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

await client.connect();

try {
  let total = 0;

  while (true) {
    const result = await client.query(
      `
      WITH target AS (
        SELECT id, name
        FROM users
        WHERE full_name IS NULL
          AND name IS NOT NULL
        ORDER BY id
        LIMIT $1
        FOR UPDATE SKIP LOCKED
      )
      UPDATE users AS u
      SET
        full_name = target.name,
        display_name = CASE
          WHEN length(target.name) > 40 THEN substring(target.name from 1 for 39) || '...'
          ELSE target.name
        END
      FROM target
      WHERE u.id = target.id
      RETURNING u.id
      `,
      [batchSize],
    );

    total += result.rowCount;
    console.log(`updated=${result.rowCount} total=${total}`);

    if (result.rowCount === 0) break;
    await sleep(sleepMs);
  }
} finally {
  await client.end();
}

Avant de l’utiliser, relisez l’idempotence, la reprise après échec, l’exécution parallèle et les contrôles opérationnels. Un bon backfill a une taille de lot, une pause, des logs, une condition d’arrêt et une requête qui peut être rejouée sans corrompre les données.

CI et staging

La CI doit appliquer tout l’historique de migrations sur une base temporaire. Dans GitHub Actions, les workflows sont des fichiers YAML sous .github/workflows.

name: migration-check

on:
  pull_request:
    paths:
      - "prisma/**"
      - "scripts/backfill-*.mjs"
      - ".github/workflows/migration-check.yml"

jobs:
  prisma-migrations:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: app
        ports:
          - "5432:5432"
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    env:
      DATABASE_URL: postgresql://postgres:postgres@localhost:5432/app?schema=public

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: npm
      - run: npm ci
      - run: npx prisma validate
      - run: npx prisma migrate deploy
      - run: npx prisma migrate status
      - name: Detect schema drift after migrations
        run: |
          npx prisma migrate diff \
            --exit-code \
            --from-config-datasource \
            --to-schema=prisma/schema.prisma

Cet exemple utilise la syntaxe Prisma ORM v7 avec config datasource. Ne recopiez pas d’anciens flags comme --from-url ou --shadow-database-url sans vérifier la référence actuelle.

Le staging doit être plus réaliste que la CI : volume de lignes comparable, index similaires, timeouts proches et même runner de migration. Demandez à Claude Code de produire une checklist avec attente de verrous, retard de réplication, latence, logs applicatifs et seuils d’abandon.

Contract et limites du rollback

Ne faites contract qu’après stabilité de la nouvelle application et validation du backfill. Pour NOT NULL, utilisez d’abord une contrainte de vérification.

-- 20260602120000_contract_users_names.sql
-- Run only after the new application version has been stable in production.

ALTER TABLE users
  ADD CONSTRAINT users_full_name_present
  CHECK (full_name IS NOT NULL) NOT VALID;

ALTER TABLE users
  VALIDATE CONSTRAINT users_full_name_present;

ALTER TABLE users
  ALTER COLUMN full_name SET NOT NULL;

ALTER TABLE users
  DROP CONSTRAINT users_full_name_present;

-- Drop old columns in a later deploy, not in the same deploy that changes reads.
-- ALTER TABLE users DROP COLUMN name;

La mauvaise hypothèse la plus dangereuse consiste à croire qu’une down migration restaure tout. Une colonne supprimée, une valeur écrasée ou une conversion avec perte ne reviennent pas par magie. Le rollback réaliste est souvent applicatif : version précédente ou feature flag. Côté base, il faut penser backup, point-in-time recovery ou correction vers l’avant.

prisma migrate resolve --rolled-back ne défait pas non plus une migration réussie. Cette commande sert à résoudre l’état de l’historique après une migration échouée. Demandez à Claude Code de séparer rollback applicatif, correction DB vers l’avant et restauration de données.

Échecs fréquents et workflow d’équipe

Premier échec : traiter un rename comme un drop and add. L’ORM ne lit pas toujours votre intention, donc relisez le SQL généré. Deuxième échec : mélanger changement de schéma et réécriture massive de données. Troisième échec : faire trop confiance à la shadow database, qui ne simule pas les données de production, le bloat, les files de locks ou le retard de réplication. Quatrième échec : ne pas vérifier concrètement les backups.

Écrivez les règles DB dans CLAUDE.md : pas de suppression de colonne de table chaude dans le même PR, pas de gros backfill dans une migration de schéma, migrations Prisma générées avec --create-only pour review SQL, liens officiels dans la review. Pour la structure, utilisez le guide CLAUDE.md best practices.

Dans un SaaS qui facture réellement, une mauvaise migration peut stopper onboarding, paiement et support. Les produits ClaudeCodeLab proposent prompts et checklists réutilisables, et la formation Claude Code aide à installer ce flux sur un vrai dépôt.

En pratique, le plus grand gain ne vient pas d’un SQL plus astucieux. Il vient du fait que chaque PR fait moins de choses. Un PR d’expand, un job de backfill séparé et un PR de contract rendent la sortie de Claude Code plus facile à relire. Avant de demander le SQL, demandez toujours les conditions d’arrêt et les étapes de récupération.

Résumé

Claude Code ne rend pas une migration de production automatiquement sûre. Il devient puissant quand l’équipe lui donne un modèle sûr : expand/contract, déploiement applicatif par étapes, petits backfills, CI, répétition en staging, feature flags et limites claires de rollback.

Pour votre prochaine migration, demandez d’abord une review des risques. Si le plan tient, laissez ensuite Claude Code écrire le SQL, la migration Prisma, le workflow GitHub Actions et le script de backfill.

#Claude Code #database migration #Prisma #PostgreSQL #CI/CD
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.