Best fit
- Buenas prácticas de migración de base de datos para cambios de esquema, migraciones de datos, rollbacks y despliegues de tiempo cero en PostgreSQL, MySQL y ORMs comunes (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate).
affaan-m/ECC
Buenas prácticas de migración de base de datos para cambios de esquema, migraciones de datos, rollbacks y despliegues de tiempo cero en PostgreSQL, MySQL y ORMs comunes (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate).
npx skills add https://github.com/affaan-m/ECC --skill "docs/es/skills/database-migrations"Source checked Jul 28, 2026·Refresh due Oct 26, 2026
Reorganized from the pinned upstream SKILL.md
According to the pinned SKILL.md from affaan-m/ECC: Cambios de esquema de base de datos seguros y reversibles para sistemas de producción.
npx skills add https://github.com/affaan-m/ECC --skill "docs/es/skills/database-migrations"Best fit
Bring this context
Expected outputs
Key source sections
Sections are extracted automatically from the pinned SKILL.md and link back to the source.
Crear o alterar tablas de base de datos
1. Cada cambio es una migración — nunca alterar 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 de datos son separadas — nunca mezclar DDL y D…
Antes de aplicar cualquier migración:
Nunca renombrar directamente en producción. Usar el patrón expand-contract:
Review the “Agregar una Columna de Forma Segura” section in the pinned source before continuing.
SkillSignal prompt templates
These prompts were written by SkillSignal from the source structure; they are not upstream text.
Task-start prompt
Confirm source fit, inputs, and outputs before acting.
Use database-migrations to help me with: [specific task]. Context: [files, data, or background]. Constraints: [environment, scope, and prohibited actions]. Before acting, check the pinned SKILL.md and explain which sections apply, what inputs are still missing, and what you will deliver.
Source-guided execution
Make the Agent explicitly follow the key extracted sections.
Apply the pinned database-migrations source to [task]. Pay particular attention to these source sections: “Cuándo Activar”, “Principios Fundamentales”, “Lista de Verificación de Seguridad de Migración”, “Patrones PostgreSQL”, “Agregar una Columna de Forma Segura”. Preserve the important decision at each step. Mark facts not covered by the source as “needs confirmation” instead of inventing them. Then verify the result against my acceptance criteria: [criteria].
Result-review prompt
Check omissions, permissions, and source drift before delivery.
Review the current database-migrations result: (1) does it satisfy the original task; (2) were any applicable steps or limits in the pinned SKILL.md missed; (3) did it perform any unauthorized file, command, network, or data action; and (4) which conclusions remain unverified? List issues first, then fix only what the source or user authorization supports.
Output checklist
The task matches the purpose documented in the SKILL.md.
The source section “Cuándo Activar” has been checked.
The source section “Principios Fundamentales” has been checked.
The source section “Lista de Verificación de Seguridad de Migración” has been checked.
The source section “Patrones PostgreSQL” has been checked.
Inputs, constraints, and acceptance criteria are explicit.
Unverified facts, compatibility, and outcome claims are clearly marked.
Any file, command, network, or data action has been reviewed.
Choose a different workflow
Database migration best practices for schema changes, data migrations, rollbacks, and zero-downtime deployments across PostgreSQL, MySQL, and common ORMs (Prisma, Drizzle, Django, TypeORM, golang-migrate). Use when planning or implementing database schema changes.
A separate implementation from affaan-m/ECC; compare its source, maintenance signals, and permission requirements.
Open source detailDatabase migration best practices for schema changes, data migrations, rollbacks, and zero-downtime deployments across PostgreSQL, MySQL, and common ORMs (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate).
A separate implementation from affaan-m/ECC; compare its source, maintenance signals, and permission requirements.
Open source detail数据库迁移最佳实践,涵盖模式变更、数据迁移、回滚以及零停机部署,适用于PostgreSQL、MySQL及常用ORM(Prisma、Drizzle、Django、TypeORM、golang-migrate)。
A separate implementation from affaan-m/ECC; compare its source, maintenance signals, and permission requirements.
Open source detailFAQ
Cambios de esquema de base de datos seguros y reversibles para sistemas de producción.
The catalog detected this source-specific install command: npx skills add https://github.com/affaan-m/ECC --skill "docs/es/skills/database-migrations". Inspect the command and pinned source before running it.
No dedicated Agent platform is declared in the pinned source record.
Quality breakdown
Based on traceable docs and repository signals; stars are not treated as quality.
Compare before choosing
These links are selected from shared tasks, functions, stacks, platforms, and same-name variants. Compare the source owner, documentation, permissions, and maintenance signals.
Database migration best practices for schema changes, data migrations, rollbacks, and zero-downtime deployments across PostgreSQL, MySQL, and common ORMs (Prisma, Drizzle, Django, TypeORM, golang-migrate). Use when planning or implementing database schema changes.
Database migration best practices for schema changes, data migrations, rollbacks, and zero-downtime deployments across PostgreSQL, MySQL, and common ORMs (Prisma, Drizzle, Kysely, Django, TypeORM, golang-migrate).
数据库迁移最佳实践,涵盖模式变更、数据迁移、回滚以及零停机部署,适用于PostgreSQL、MySQL及常用ORM(Prisma、Drizzle、Django、TypeORM、golang-migrate)。
Şema değişiklikleri, veri migration'ları, rollback'ler ve PostgreSQL, MySQL ve yaygın ORM'ler (Prisma, Drizzle, Django, TypeORM, golang-migrate) arasında sıfır kesinti deployment'ları için veritabanı migration en iyi uygulamaları.
PostgreSQL、MySQL、一般的なORM(Prisma、Drizzle、Kysely、Django、TypeORM、golang-migrate)全体のスキーマ変更、データマイグレーション、ロールバック、ゼロダウンタイムデプロイメントのためのデータベースマイグレーションベストプラクティス。
Cambios de esquema de base de datos seguros y reversibles para sistemas de producción.
Antes de aplicar cualquier migración:
-- BIEN: Columna nullable, sin bloqueo
ALTER TABLE users ADD COLUMN avatar_url TEXT;
-- BIEN: Columna con valor predeterminado (Postgres 11+ es instantáneo, sin reescritura)
ALTER TABLE users ADD COLUMN is_active BOOLEAN NOT NULL DEFAULT true;
-- MAL: 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
-- 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 herramientas de migración necesitan manejo especial para esto
Nunca renombrar directamente en producción. Usar el patrón expand-contract:
-- Paso 1: Agregar nueva columna (migración 001)
ALTER TABLE users ADD COLUMN display_name TEXT;
-- Paso 2: Backfill de 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 aplicación
-- Paso 4: Dejar de escribir en la columna antigua, eliminarla (migración 003)
ALTER TABLE users DROP COLUMN username;
-- Paso 1: Eliminar todas las referencias de la aplicación a la columna
-- Paso 2: Desplegar la aplicación sin la referencia a la columna
-- Paso 3: Eliminar la columna en la próxima 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 próxima migración)
-- MAL: Actualiza todas las filas en una transacción (bloquea la tabla)
UPDATE users SET normalized_email = LOWER(email);
-- BIEN: Actualización en 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 'Updated % rows', rows_updated;
EXIT WHEN rows_updated = 0;
COMMIT;
END LOOP;
END $$;
# 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
# Resetear base de datos (solo desarrollo)
npx prisma migrate reset
# Generar cliente después de cambios de esquema
npx prisma generate
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])
}
Para operaciones que Prisma no puede expresar (índices concurrentes, backfills 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, por lo que se escribe manualmente
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email);
# Generar migración a partir de cambios de esquema
npx drizzle-kit generate
# Aplicar migraciones
npx drizzle-kit migrate
# Hacer push del esquema directamente (solo desarrollo, sin archivo de migración)
npx drizzle-kit push
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(),
});
# 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
// migrations/2024_01_15_001_create_user_profile.ts
import { type Kysely, sql } from 'kysely'
// IMPORTANTE: Siempre usar Kysely<any>, no tu interfaz de 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()
}
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. Deshabilita la validación de
// ordenamiento por timestamp, lo que puede causar deriva de esquema entre entornos.
// allowUnorderedMigrations: true,
})
const { error, results } = await migrator.migrateToLatest()
results?.forEach((it) => {
if (it.status === 'Success') {
console.log(`migration "${it.migrationName}" executed successfully`)
} else if (it.status === 'Error') {
console.error(`failed to execute migration "${it.migrationName}"`)
}
})
if (error) {
console.error('migration failed', error)
process.exit(1)
}
# 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
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),
]
Eliminar una columna del modelo 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 DB todavía
),
]
# Crear par de migración
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
-- 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;
Para cambios críticos de producción, seguir el patrón expand-contract:
Fase 1: EXPAND (Expandir)
- Agregar nueva columna/tabla (nullable o con valor predeterminado)
- Desplegar: la app escribe en AMBAS, vieja y nueva
- Backfill de datos existentes
Fase 2: MIGRATE (Migrar)
- Desplegar: la app lee de la NUEVA, escribe en AMBAS
- Verificar consistencia de datos
Fase 3: CONTRACT (Contraer)
- Desplegar: la app solo usa la NUEVA
- Eliminar columna/tabla antigua en migración separada
Día 1: Migración agrega columna new_status (nullable)
Día 1: Desplegar app v2 — escribe en status y new_status
Día 2: Ejecutar migración de backfill para filas existentes
Día 3: Desplegar app v3 — lee solo de new_status
Día 7: Migración elimina columna status antigua
| Anti-Patrón | Por Qué Falla | Mejor Enfoque |
|---|---|---|
| SQL manual en producción | Sin historial de auditoría, no repetible | Siempre usar archivos de migración |
| Editar migraciones desplegadas | Causa deriva entre entornos | Crear nueva migración en su lugar |
| NOT NULL sin valor predeterminado | Bloquea tabla, reescribe todas las filas | Agregar nullable, backfill, luego agregar restricción |
| Índice en línea en tabla grande | Bloquea escrituras durante la construcción | CREATE INDEX CONCURRENTLY |
| Esquema + datos en una migración | Difícil de revertir, transacciones largas | Migraciones separadas |
| Eliminar columna antes de eliminar código | Errores de aplicación por columna faltante | Eliminar código primero, eliminar columna en el próximo despliegue |