Communitygithub.com

migrations-de-base-de-données

Bonnes pratiques pour les migrations de base de données : modifications de schéma, migrations de données, rollbacks et déploiements sans interruption pour PostgreSQL, MySQL et les ORM courants (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate). À utiliser lors de l'écriture d'une migration de schéma ou de données, de la planification d'un rollback ou pour un déploiement sans interruption.

Qu'est-ce que migrations-de-base-de-données ?

Cette compétence fournit un guide complet et structuré sur les bonnes pratiques de migration de bases de données, couvrant PostgreSQL, MySQL et plusieurs ORM populaires (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate). Elle explique les principes fondamentaux tels que le pattern expand-contract pour les modifications sans interruption, la séparation stricte entre migrations de schéma et de données, et l'immutabilité des migrations en production. Le guide inclut des checklists de sécurité, des exemples concrets de code SQL et des workflows spécifiques à chaque outil, ainsi que des anti-patrons à éviter. L'objectif est d'aider les développeurs et les systèmes d'IA à concevoir, exécuter et surtout planifier des migrations sûres, réversibles et optimales pour les systèmes de production, en minimisant les risques de verrouillage, de perte de données ou d'interruption de service.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/database-migrations

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Modèles de Migration de Base de Données

Modifications de schéma de base de données sûres et réversibles pour les systèmes de production.

Quand Activer

  • Créer ou modifier des tables de base de données
  • Ajouter/supprimer des colonnes ou des index
  • Exécuter des migrations de données (backfill, transformation)
  • Planifier des modifications de schéma sans interruption
  • Mettre en place un outillage de migration pour un nouveau projet

Principes Fondamentaux

  1. Chaque modification est une migration — ne modifiez jamais manuellement les bases de données de production
  2. Les migrations sont forward-only en production — les rollbacks utilisent de nouvelles migrations forward
  3. Les migrations de schéma et de données sont séparées — ne mélangez jamais DDL et DML dans une même migration
  4. Testez les migrations sur des données de taille production — une migration qui fonctionne sur 100 lignes peut bloquer sur 10M
  5. Les migrations sont immuables une fois déployées — ne modifiez jamais une migration qui a été exécutée en production

Liste de Vérification de Sécurité des Migrations

Avant d'appliquer une migration :

  • La migration a UP et DOWN (ou est explicitement marquée irréversible)
  • Pas de verrouillage complet de table sur les grandes tables (utilisez des opérations concurrentes)
  • Les nouvelles colonnes ont des valeurs par défaut ou acceptent NULL (n'ajoutez jamais NOT NULL sans valeur par défaut)
  • Les index créés de manière concurrente (pas en ligne avec CREATE TABLE pour les tables existantes)
  • Le backfill de données est une migration distincte du changement de schéma
  • Testé sur une copie des données de production
  • Plan de rollback documenté

Modèles PostgreSQL

Ajouter une Colonne en Toute Sécurité

-- BON : Colonne nullable, pas de verrou
ALTER TABLE users ADD COLUMN avatar_url TEXT;

-- BON : Colonne avec valeur par défaut (Postgres 11+ est instantané, pas de réécriture)
ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT true;

-- MAUVAIS : NOT NULL sans valeur par défaut sur une table existante (nécessite une réécriture complète)
ALTER TABLE users ADD COLUMN role TEXT NOT NULL;
-- Cela verrouille la table et réécrit chaque ligne

Ajouter un Index Sans Interruption

-- MAUVAIS : Bloque les écritures sur les grandes tables
CREATE INDEX idx_users_email ON users (email);

-- BON : Non bloquant, permet les écritures concurrentes
CREATE INDEX CONCURRENTLY idx_users_email ON users (email);

-- Note : CONCURRENTLY ne peut pas s'exécuter dans un bloc de transaction
-- La plupart des outils de migration nécessitent une gestion spéciale

Renommer une Colonne (Sans Interruption)

Ne renommez jamais directement en production. Utilisez le pattern expand-contract :

-- Étape 1 : Ajouter la nouvelle colonne (migration 001)
ALTER TABLE users ADD COLUMN display_name TEXT;

-- Étape 2 : Backfill des données (migration 002, migration de données)
UPDATE users SET display_name = username WHERE display_name IS NULL;

-- Étape 3 : Mettre à jour le code de l'application pour lire/écrire les deux colonnes
-- Déployer les modifications de l'application

-- Étape 4 : Arrêter d'écrire dans l'ancienne colonne, la supprimer (migration 003)
ALTER TABLE users DROP COLUMN username;

Supprimer une Colonne en Toute Sécurité

-- Étape 1 : Supprimer toutes les références à la colonne dans l'application
-- Étape 2 : Déployer l'application sans la référence à la colonne
-- Étape 3 : Supprimer la colonne dans la migration suivante
ALTER TABLE orders DROP COLUMN legacy_status;

-- Pour Django : utiliser SeparateDatabaseAndState pour supprimer du modèle
-- sans générer DROP COLUMN (puis supprimer dans la migration suivante)

Migrations de Données Importantes

-- MAUVAIS : Met à jour toutes les lignes dans une même transaction (verrouille la table)
UPDATE users SET normalized_email = LOWER(email);

-- BON : Mise à jour par lots avec progression
DO $$
DECLARE
  batch_size INT := 10000;
  rows_updated INT;
BEGIN
  LOOP
    UPDATE users
    SET normalized_email = LOWER(email)
    WHERE id IN (
      SELECT id FROM users
      WHERE normalized_email IS NULL
      LIMIT batch_size
      FOR UPDATE SKIP LOCKED
    );
    GET DIAGNOSTICS rows_updated = ROW_COUNT;
    RAISE NOTICE 'Mis à jour % lignes', rows_updated;
    EXIT WHEN rows_updated = 0;
    COMMIT;
  END LOOP;
END $$;

Prisma (TypeScript/Node.js)

Workflow

# Créer une migration à partir des modifications de schéma
npx prisma migrate dev --name add_user_avatar

# Appliquer les migrations en attente en production
npx prisma migrate deploy

# Réinitialiser la base de données (développement uniquement)
npx prisma migrate reset

# Générer le client après les modifications de schéma
npx prisma generate

Exemple de Schéma

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  avatarUrl String?  @map("avatar_url")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")
  orders    Order[]

  @@map("users")
  @@index([email])
}

Migration SQL Personnalisée

Pour les opérations que Prisma ne peut pas exprimer (index concurrents, backfills de données) :

# Créer une migration vide, puis éditer le SQL manuellement
npx prisma migrate dev --create-only --name add_email_index
-- migrations/20240115_add_email_index/migration.sql
-- Prisma ne peut pas générer CONCURRENTLY, on l'écrit donc manuellement
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email);

Drizzle (TypeScript/Node.js)

Workflow

# Générer la migration à partir des modifications de schéma
npx drizzle-kit generate

# Appliquer les migrations
npx drizzle-kit migrate

# Pousser le schéma directement (développement uniquement, pas de fichier de migration)
npx drizzle-kit push

Exemple de Schéma

import { pgTable, text, timestamp, uuid, boolean } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: uuid("id").primaryKey().defaultRandom(),
  email: text("email").notNull().unique(),
  name: text("name"),
  isActive: boolean("is_active").notNull().default(true),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at").notNull().defaultNow(),
});

Kysely (TypeScript/Node.js)

Workflow (kysely-ctl)

# Initialiser le fichier de configuration (kysely.config.ts)
kysely init

# Créer un nouveau fichier de migration
kysely migrate make add_user_avatar

# Appliquer toutes les migrations en attente
kysely migrate latest

# Revenir à la migration précédente
kysely migrate down

# Afficher le statut des migrations
kysely migrate list

Fichier de Migration

// migrations/2024_01_15_001_create_user_profile.ts
import { type Kysely, sql } from 'kysely'

// IMPORTANT : Utilisez toujours Kysely<any>, pas votre interface DB typée.
// Les migrations sont figées dans le temps et ne doivent pas dépendre des types de schéma actuels.
export async function up(db: Kysely<any>): Promise<void> {
  await db.schema
    .createTable('user_profile')
    .addColumn('id', 'serial', (col) => col.primaryKey())
    .addColumn('email', 'varchar(255)', (col) => col.notNull().unique())
    .addColumn('avatar_url', 'text')
    .addColumn('created_at', 'timestamp', (col) =>
      col.defaultTo(sql`now()`).notNull()
    )
    .execute()

  await db.schema
    .createIndex('idx_user_profile_avatar')
    .on('user_profile')
    .column('avatar_url')
    .execute()
}

export async function down(db: Kysely<any>): Promise<void> {
  await db.schema.dropTable('user_profile').execute()
}

Migrateur Programmateur

import { Migrator, FileMigrationProvider } from 'kysely'
import { promises as fs } from 'fs'
import * as path from 'path'
// ESM uniquement — CJS peut utiliser __dirname directement
import { fileURLToPath } from 'url'
const migrationFolder = path.join(
  path.dirname(fileURLToPath(import.meta.url)),
  './migrations',
)

// `db` est votre instance de base de données Kysely<any>
const migrator = new Migrator({
  db,
  provider: new FileMigrationProvider({
    fs,
    path,
    migrationFolder,
  }),
  // ATTENTION : Activer uniquement en développement. Désactive la validation
  // de l'ordre des horodatages, ce qui peut entraîner une dérive de schéma entre les environnements.
  // allowUnorderedMigrations: true,
})

const { error, results } = await migrator.migrateToLatest()

results?.forEach((it) => {
  if (it.status === 'Success') {
    console.log(`migration "${it.migrationName}" exécutée avec succès`)
  } else if (it.status === 'Error') {
    console.error(`échec d'exécution de la migration "${it.migrationName}"`)
  }
})

if (error) {
  console.error('échec de la migration', error)
  process.exit(1)
}

Django (Python)

Workflow

# Générer la migration à partir des modifications du modèle
python manage.py makemigrations

# Appliquer les migrations
python manage.py migrate

# Afficher le statut des migrations
python manage.py showmigrations

# Générer une migration vide pour du SQL personnalisé
python manage.py makemigrations --empty app_name -n description

Migration de Données

from django.db import migrations

def backfill_display_names(apps, schema_editor):
    User = apps.get_model("accounts", "User")
    batch_size = 5000
    users = User.objects.filter(display_name="")
    while users.exists():
        batch = list(users[:batch_size])
        for user in batch:
            user.display_name = user.username
        User.objects.bulk_update(batch, ["display_name"], batch_size=batch_size)

def reverse_backfill(apps, schema_editor):
    pass  # Migration de données, pas de reverse nécessaire

class Migration(migrations.Migration):
    dependencies = [("accounts", "0015_add_display_name")]

    operations = [
        migrations.RunPython(backfill_display_names, reverse_backfill),
    ]

SeparateDatabaseAndState

Supprimer une colonne du modèle Django sans la supprimer immédiatement de la base de données :

class Migration(migrations.Migration):
    operations = [
        migrations.SeparateDatabaseAndState(
            state_operations=[
                migrations.RemoveField(model_name="user", name="legacy_field"),
            ],
            database_operations=[],  # Ne pas toucher à la BD pour le moment
        ),
    ]

golang-migrate (Go)

Workflow

# Créer une paire de migration
migrate create -ext sql -dir migrations -seq add_user_avatar

# Appliquer toutes les migrations en attente
migrate -path migrations -database "$DATABASE_URL" up

# Revenir à la migration précédente
migrate -path migrations -database "$DATABASE_URL" down 1

# Forcer une version (corriger un état sale)
migrate -path migrations -database "$DATABASE_URL" force VERSION

Fichiers de Migration

-- migrations/000003_add_user_avatar.up.sql
ALTER TABLE users ADD COLUMN avatar_url TEXT;
CREATE INDEX CONCURRENTLY idx_users_avatar ON users (avatar_url) WHERE avatar_url IS NOT NULL;

-- migrations/000003_add_user_avatar.down.sql
DROP INDEX IF EXISTS idx_users_avatar;
ALTER TABLE users DROP COLUMN IF EXISTS avatar_url;

Stratégie de Migration Sans Interruption

Pour les modifications critiques en production, suivez le pattern expand-contract :

Phase 1 : EXPAND
  - Ajouter une nouvelle colonne/table (nullable ou avec valeur par défaut)
  - Déployer : l'application écrit dans les DEUX, ancienne et nouvelle
  - Backfill des données existantes

Phase 2 : MIGRATE
  - Déployer : l'application lit de la NOUVELLE, écrit dans les DEUX
  - Vérifier la cohérence des données

Phase 3 : CONTRACT
  - Déployer : l'application utilise uniquement la NOUVELLE
  - Supprimer l'ancienne colonne/table dans une migration séparée

Exemple de Calendrier

Jour 1 : Migration ajoute la colonne new_status (nullable)
Jour 1 : Déployer l'application v2 — écrit dans status et new_status
Jour 2 : Exécuter la migration de backfill pour les lignes existantes
Jour 3 : Déployer l'application v3 — lit uniquement de new_status
Jour 7 : Migration supprime l'ancienne colonne status

Anti-Patrons

Anti-PatronPourquoi Ça ÉchoueMeilleure Approche
SQL manuel en productionPas de piste d'audit, non reproductibleToujours utiliser des fichiers de migration
Modifier des migrations déployéesProvoque une dérive entre les environnementsCréer une nouvelle migration à la place
NOT NULL sans valeur par défautVerrouille la table, réécrit toutes les lignesAjouter nullable, faire un backfill, puis ajouter la contrainte
Index en ligne sur grande tableBloque les écritures pendant la constructionCREATE INDEX CONCURRENTLY
Schéma + données dans une même migrationDifficile à rollback, transactions longuesMigrations séparées
Supprimer une colonne avant de supprimer le codeErreurs d'application sur colonne manquanteSupprimer le code d'abord, supprimer la colonne au prochain déploiement

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills associés