Communitygithub.com

database-migrations

Prácticas recomendadas para migraciones de base de datos: cambios de esquema, migraciones de datos, rollbacks y despliegues sin tiempo de inactividad en PostgreSQL, MySQL y ORMs comunes (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate). Úsalo al escribir una migración de esquema o datos, planificar un rollback o buscar un despliegue sin tiempo de inactividad.

¿Qué es database-migrations?

Este skill proporciona una guía completa para gestionar migraciones de bases de datos de manera segura y reversible en sistemas de producción. Cubre principios fundamentales como la inmutabilidad de las migraciones una vez desplegadas, la separación entre cambios de esquema y datos, y la importancia de probar contra datos del tamaño de producción. Incluye patrones específicos para PostgreSQL (agregar columnas de forma segura, índices concurrentes, renombrar columnas sin tiempo de inactividad, migraciones de datos por lotes) y guías detalladas para ORMs como Prisma, Drizzle, Kysely, Django y golang-migrate. También presenta el patrón expandir-contraer para cambios críticos con tiempo de inactividad cero, una checklist de seguridad antes de cada migración, y una tabla de antipatrones comunes. Es útil para desarrolladores que trabajan con TypeScript, Python, Go, u otros lenguajes, y necesitan automatizar o auditar cambios en bases de datos en producción.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones de Migración de Base de Datos

Cambios seguros y reversibles en el esquema de base de datos para sistemas de producción.

Cuándo Activar

  • Crear o modificar tablas de base de datos
  • Agregar/eliminar columnas o índices
  • Ejecutar migraciones de datos (relleno, transformación)
  • Planificar cambios de esquema sin tiempo de inactividad
  • Configurar herramientas de migración para un nuevo proyecto

Principios Fundamentales

  1. Cada cambio es una migración — nunca modifiques bases de datos de producción manualmente
  2. Las migraciones son solo hacia adelante en producción — los rollbacks usan nuevas migraciones hacia adelante
  3. Las migraciones de esquema y datos son separadas — nunca mezcles DDL y DML en una misma migración
  4. Prueba las migraciones con datos del tamaño de producción — una migración que funciona con 100 filas puede bloquearse con 10M
  5. Las migraciones son inmutables una vez desplegadas — nunca edites una migración que se haya ejecutado en producción

Lista de Verificación de Seguridad de Migración

Antes de aplicar cualquier migración:

  • La migración tiene UP y DOWN (o está marcada explícitamente como irreversible)
  • Sin bloqueos completos de tabla en tablas grandes (usa operaciones concurrentes)
  • Las nuevas columnas tienen valores por defecto o son anulables (nunca agregues NOT NULL sin valor por defecto)
  • Índices creados concurrentemente (no en línea con CREATE TABLE para tablas existentes)
  • El relleno de datos es una migración separada del cambio de esquema
  • Probado contra una copia de los datos de producción
  • Plan de reversión documentado

Patrones para PostgreSQL

Agregar una Columna de Forma Segura

-- BIEN: Columna anulable, sin bloqueo
ALTER TABLE users ADD COLUMN avatar_url TEXT;

-- BIEN: Columna con valor por defecto (Postgres 11+ es instantáneo, sin reescritura)
ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT true;

-- MAL: NOT NULL sin valor por defecto en tabla existente (requiere reescritura completa)
ALTER TABLE users ADD COLUMN role TEXT NOT NULL;
-- Esto bloquea la tabla y reescribe cada fila

Agregar un Índice Sin Tiempo de Inactividad

-- MAL: Bloquea escrituras en tablas grandes
CREATE INDEX idx_users_email ON users (email);

-- BIEN: No bloqueante, permite escrituras concurrentes
CREATE INDEX CONCURRENTLY idx_users_email ON users (email);

-- Nota: CONCURRENTLY no puede ejecutarse dentro de un bloque de transacción
-- La mayoría de las herramientas de migración necesitan manejo especial para esto

Renombrar una Columna (Tiempo de Inactividad Cero)

Nunca renombres directamente en producción. Usa el patrón expandir-contraer:

-- Paso 1: Agregar nueva columna (migración 001)
ALTER TABLE users ADD COLUMN display_name TEXT;

-- Paso 2: Rellenar datos (migración 002, migración de datos)
UPDATE users SET display_name = username WHERE display_name IS NULL;

-- Paso 3: Actualizar código de aplicación para leer/escribir ambas columnas
-- Desplegar cambios de aplicación

-- Paso 4: Dejar de escribir en la columna antigua, eliminarla (migración 003)
ALTER TABLE users DROP COLUMN username;

Eliminar una Columna de Forma Segura

-- Paso 1: Eliminar todas las referencias de la aplicación a la columna
-- Paso 2: Desplegar aplicación sin la referencia a la columna
-- Paso 3: Eliminar columna en la siguiente migración
ALTER TABLE orders DROP COLUMN legacy_status;

-- Para Django: usa SeparateDatabaseAndState para eliminar del modelo
-- sin generar DROP COLUMN (luego eliminar en la siguiente migración)

Migraciones de Datos Grandes

-- MAL: Actualiza todas las filas en una transacción (bloquea la tabla)
UPDATE users SET normalized_email = LOWER(email);

-- BIEN: Actualización por lotes con progreso
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 'Actualizadas % filas', rows_updated;
    EXIT WHEN rows_updated = 0;
    COMMIT;
  END LOOP;
END $$;

Prisma (TypeScript/Node.js)

Flujo de Trabajo

# Crear migración a partir de cambios de esquema
npx prisma migrate dev --name add_user_avatar

# Aplicar migraciones pendientes en producción
npx prisma migrate deploy

# Restablecer base de datos (solo desarrollo)
npx prisma migrate reset

# Generar cliente después de cambios de esquema
npx prisma generate

Ejemplo de Esquema

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])
}

Migración SQL Personalizada

Para operaciones que Prisma no puede expresar (índices concurrentes, relleno de datos):

# Crear migración vacía, luego editar el SQL manualmente
npx prisma migrate dev --create-only --name add_email_index
-- migrations/20240115_add_email_index/migration.sql
-- Prisma no puede generar CONCURRENTLY, así que lo escribimos manualmente
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email);

Drizzle (TypeScript/Node.js)

Flujo de Trabajo

# Generar migración a partir de cambios de esquema
npx drizzle-kit generate

# Aplicar migraciones
npx drizzle-kit migrate

# Enviar esquema directamente (solo desarrollo, sin archivo de migración)
npx drizzle-kit push

Ejemplo de Esquema

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)

Flujo de Trabajo (kysely-ctl)

# Inicializar archivo de configuración (kysely.config.ts)
kysely init

# Crear un nuevo archivo de migración
kysely migrate make add_user_avatar

# Aplicar todas las migraciones pendientes
kysely migrate latest

# Revertir la última migración
kysely migrate down

# Mostrar estado de migraciones
kysely migrate list

Archivo de Migración

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

// IMPORTANTE: Usa siempre Kysely<any>, no tu interfaz DB tipada.
// Las migraciones están congeladas en el tiempo y no deben depender de los tipos de esquema actuales.
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()
}

Migrador Programático

import { Migrator, FileMigrationProvider } from 'kysely'
import { promises as fs } from 'fs'
import * as path from 'path'
// Solo ESM — CJS puede usar __dirname directamente
import { fileURLToPath } from 'url'
const migrationFolder = path.join(
  path.dirname(fileURLToPath(import.meta.url)),
  './migrations',
)

// `db` es tu instancia de base de datos Kysely<any>
const migrator = new Migrator({
  db,
  provider: new FileMigrationProvider({
    fs,
    path,
    migrationFolder,
  }),
  // ADVERTENCIA: Habilita solo en desarrollo. Desactiva la validación de
  // orden por marca de tiempo, lo que puede causar desviación de esquema entre entornos.
  // allowUnorderedMigrations: true,
})

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

results?.forEach((it) => {
  if (it.status === 'Success') {
    console.log(`migración "${it.migrationName}" ejecutada exitosamente`)
  } else if (it.status === 'Error') {
    console.error(`fallo al ejecutar la migración "${it.migrationName}"`)
  }
})

if (error) {
  console.error('la migración falló', error)
  process.exit(1)
}

Django (Python)

Flujo de Trabajo

# Generar migración a partir de cambios de modelo
python manage.py makemigrations

# Aplicar migraciones
python manage.py migrate

# Mostrar estado de migraciones
python manage.py showmigrations

# Generar migración vacía para SQL personalizado
python manage.py makemigrations --empty app_name -n descripcion

Migración de Datos

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  # Migración de datos, no se necesita reversión

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

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

SeparateDatabaseAndState

Eliminar una columna del modelo de Django sin eliminarla de la base de datos inmediatamente:

class Migration(migrations.Migration):
    operations = [
        migrations.SeparateDatabaseAndState(
            state_operations=[
                migrations.RemoveField(model_name="user", name="legacy_field"),
            ],
            database_operations=[],  # No tocar la BD aún
        ),
    ]

golang-migrate (Go)

Flujo de Trabajo

# Crear par de migraciones
migrate create -ext sql -dir migrations -seq add_user_avatar

# Aplicar todas las migraciones pendientes
migrate -path migrations -database "$DATABASE_URL" up

# Revertir la última migración
migrate -path migrations -database "$DATABASE_URL" down 1

# Forzar versión (corregir estado sucio)
migrate -path migrations -database "$DATABASE_URL" force VERSION

Archivos de Migración

-- 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;

Estrategia de Migración Sin Tiempo de Inactividad

Para cambios críticos en producción, sigue el patrón expandir-contraer:

Fase 1: EXPANDIR
  - Agregar nueva columna/tabla (anulable o con valor por defecto)
  - Desplegar: la aplicación escribe en AMBAS (antigua y nueva)
  - Rellenar datos existentes

Fase 2: MIGRAR
  - Desplegar: la aplicación LEE de la NUEVA, escribe en AMBAS
  - Verificar consistencia de datos

Fase 3: CONTRAER
  - Desplegar: la aplicación solo usa la NUEVA
  - Eliminar columna/tabla antigua en migración separada

Ejemplo de Cronograma

Día 1: Migración agrega columna new_status (anulable)
Día 1: Desplegar app v2 — escribe tanto en status como en new_status
Día 2: Ejecutar migración de relleno para filas existentes
Día 3: Desplegar app v3 — lee solo de new_status
Día 7: Migración elimina columna antigua status

Antipatrones

AntipatrónPor Qué FallaMejor Enfoque
SQL manual en producciónSin pista de auditoría, no repetibleSiempre usar archivos de migración
Editar migraciones desplegadasCausa desviación entre entornosCrear nueva migración en su lugar
NOT NULL sin valor por defectoBloquea tabla, reescribe todas las filasAgregar anulable, rellenar, luego agregar restricción
Índice en línea en tabla grandeBloquea escrituras durante la construcciónCREATE INDEX CONCURRENTLY
Esquema + datos en una migraciónDifícil de revertir, transacciones largasMigraciones separadas
Eliminar columna antes de eliminar códigoErrores de aplicación por columna faltanteEliminar código primero, luego eliminar columna en el próximo despliegue

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 relacionados