Communitygithub.com

migraciones-de-base-de-datos

Mejores prácticas 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 interrupciones.

¿Qué es migraciones-de-base-de-datos?

Esta habilidad proporciona pautas y patrones completos para gestionar migraciones de bases de datos de forma segura, reversible y con cero tiempo de inactividad en sistemas de producción. Está diseñada para desarrolladores que utilizan PostgreSQL, MySQL y ORMs populares como Prisma, Drizzle, Kysely, Django, TypeORM y golang-migrate. Explica principios fundamentales como la inmutabilidad de las migraciones, la separación entre cambios de esquema y datos, y el uso de operaciones concurrentes para evitar bloqueos. También detalla patrones de expansión y contracción (expand-contract) para renombrar o eliminar columnas sin afectar la disponibilidad, y proporciona ejemplos prácticos en SQL y en cada ORM soportado. Esta guía es esencial para cualquier equipo que busque automatizar y estandarizar sus procesos de migración, minimizando riesgos y garantizando la integridad de los datos en entornos críticos.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/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 Activarlo

  • 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 están separadas — nunca mezcles DDL y DML en una misma migración
  4. Prueba las migraciones contra datos de 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 Migraciones

Antes de aplicar cualquier migración:

  • La migración tiene tanto UP como DOWN (o está marcada explícitamente como irreversible)
  • Sin bloqueos completos de tabla en tablas grandes (usa operaciones concurrentes)
  • Las nuevas columnas tienen valores predeterminados o son anulables (nunca agregar NOT NULL sin valor predeterminado)
  • Los índices se crean 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 rollback documentado

Patrones para PostgreSQL

Agregar una Columna de Forma Segura

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

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

-- MALO: NOT NULL sin valor predeterminado 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

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

-- BUENO: No bloquea, 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 (Cero Tiempo de Inactividad)

Nunca renombrar directamente en producción. Usa el patrón de expansión y contracción:

-- 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 el código de la aplicación para leer/escribir ambas columnas
-- Desplegar cambios de la 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 la aplicación sin la referencia de la columna
-- Paso 3: Eliminar la columna en la siguiente migración
ALTER TABLE orders DROP COLUMN legacy_status;

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

Migraciones de Datos Grandes

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

-- BUENO: 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, rellenos 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

# Empujar 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 las migraciones
kysely migrate list

Archivo de Migración

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

// IMPORTANTE: Siempre usar 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: Solo habilitar 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('migración fallida', 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 description

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 necesita reversa

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 de expansión y contracción:

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

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

Fase 3: CONTRAER
  - Desplegar: la aplicación solo usa 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 status antigua

Anti-Patrones

Anti-PatrónPor Qué FallaMejor Enfoque
SQL manual en producciónSin registro de auditoría, no repetibleUsar siempre archivos de migración
Editar migraciones desplegadasCausa desviación entre entornosCrear nueva migración
NOT NULL sin valor predeterminadoBloquea 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 quitar códigoErrores de aplicación por columna faltanteQuitar código primero, eliminar columna en siguiente 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/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

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.

Skills relacionados