Execute database migrations across ORMs and platforms with zero-downtime strategies, data transformation, and rollback procedures. Use when migrating databases, changing schemas, performing data transformations, or implementing zero-downtime deployment strategies.
Add this skill
npx mdskills install sickn33/database-migrationComprehensive migration guide with ORM-specific examples and zero-downtime patterns
1---2name: database-migration3description: Execute database migrations across ORMs and platforms with zero-downtime strategies, data transformation, and rollback procedures. Use when migrating databases, changing schemas, performing data transformations, or implementing zero-downtime deployment strategies.4---56# Database Migration78Master database schema and data migrations across ORMs (Sequelize, TypeORM, Prisma), including rollback strategies and zero-downtime deployments.910## Do not use this skill when1112- The task is unrelated to database migration13- You need a different domain or tool outside this scope1415## Instructions1617- Clarify goals, constraints, and required inputs.18- Apply relevant best practices and validate outcomes.19- Provide actionable steps and verification.20- If detailed examples are required, open `resources/implementation-playbook.md`.2122## Use this skill when2324- Migrating between different ORMs25- Performing schema transformations26- Moving data between databases27- Implementing rollback procedures28- Zero-downtime deployments29- Database version upgrades30- Data model refactoring3132## ORM Migrations3334### Sequelize Migrations35```javascript36// migrations/20231201-create-users.js37module.exports = {38 up: async (queryInterface, Sequelize) => {39 await queryInterface.createTable('users', {40 id: {41 type: Sequelize.INTEGER,42 primaryKey: true,43 autoIncrement: true44 },45 email: {46 type: Sequelize.STRING,47 unique: true,48 allowNull: false49 },50 createdAt: Sequelize.DATE,51 updatedAt: Sequelize.DATE52 });53 },5455 down: async (queryInterface, Sequelize) => {56 await queryInterface.dropTable('users');57 }58};5960// Run: npx sequelize-cli db:migrate61// Rollback: npx sequelize-cli db:migrate:undo62```6364### TypeORM Migrations65```typescript66// migrations/1701234567-CreateUsers.ts67import { MigrationInterface, QueryRunner, Table } from 'typeorm';6869export class CreateUsers1701234567 implements MigrationInterface {70 public async up(queryRunner: QueryRunner): Promise<void> {71 await queryRunner.createTable(72 new Table({73 name: 'users',74 columns: [75 {76 name: 'id',77 type: 'int',78 isPrimary: true,79 isGenerated: true,80 generationStrategy: 'increment'81 },82 {83 name: 'email',84 type: 'varchar',85 isUnique: true86 },87 {88 name: 'created_at',89 type: 'timestamp',90 default: 'CURRENT_TIMESTAMP'91 }92 ]93 })94 );95 }9697 public async down(queryRunner: QueryRunner): Promise<void> {98 await queryRunner.dropTable('users');99 }100}101102// Run: npm run typeorm migration:run103// Rollback: npm run typeorm migration:revert104```105106### Prisma Migrations107```prisma108// schema.prisma109model User {110 id Int @id @default(autoincrement())111 email String @unique112 createdAt DateTime @default(now())113}114115// Generate migration: npx prisma migrate dev --name create_users116// Apply: npx prisma migrate deploy117```118119## Schema Transformations120121### Adding Columns with Defaults122```javascript123// Safe migration: add column with default124module.exports = {125 up: async (queryInterface, Sequelize) => {126 await queryInterface.addColumn('users', 'status', {127 type: Sequelize.STRING,128 defaultValue: 'active',129 allowNull: false130 });131 },132133 down: async (queryInterface) => {134 await queryInterface.removeColumn('users', 'status');135 }136};137```138139### Renaming Columns (Zero Downtime)140```javascript141// Step 1: Add new column142module.exports = {143 up: async (queryInterface, Sequelize) => {144 await queryInterface.addColumn('users', 'full_name', {145 type: Sequelize.STRING146 });147148 // Copy data from old column149 await queryInterface.sequelize.query(150 'UPDATE users SET full_name = name'151 );152 },153154 down: async (queryInterface) => {155 await queryInterface.removeColumn('users', 'full_name');156 }157};158159// Step 2: Update application to use new column160161// Step 3: Remove old column162module.exports = {163 up: async (queryInterface) => {164 await queryInterface.removeColumn('users', 'name');165 },166167 down: async (queryInterface, Sequelize) => {168 await queryInterface.addColumn('users', 'name', {169 type: Sequelize.STRING170 });171 }172};173```174175### Changing Column Types176```javascript177module.exports = {178 up: async (queryInterface, Sequelize) => {179 // For large tables, use multi-step approach180181 // 1. Add new column182 await queryInterface.addColumn('users', 'age_new', {183 type: Sequelize.INTEGER184 });185186 // 2. Copy and transform data187 await queryInterface.sequelize.query(`188 UPDATE users189 SET age_new = CAST(age AS INTEGER)190 WHERE age IS NOT NULL191 `);192193 // 3. Drop old column194 await queryInterface.removeColumn('users', 'age');195196 // 4. Rename new column197 await queryInterface.renameColumn('users', 'age_new', 'age');198 },199200 down: async (queryInterface, Sequelize) => {201 await queryInterface.changeColumn('users', 'age', {202 type: Sequelize.STRING203 });204 }205};206```207208## Data Transformations209210### Complex Data Migration211```javascript212module.exports = {213 up: async (queryInterface, Sequelize) => {214 // Get all records215 const [users] = await queryInterface.sequelize.query(216 'SELECT id, address_string FROM users'217 );218219 // Transform each record220 for (const user of users) {221 const addressParts = user.address_string.split(',');222223 await queryInterface.sequelize.query(224 `UPDATE users225 SET street = :street,226 city = :city,227 state = :state228 WHERE id = :id`,229 {230 replacements: {231 id: user.id,232 street: addressParts[0]?.trim(),233 city: addressParts[1]?.trim(),234 state: addressParts[2]?.trim()235 }236 }237 );238 }239240 // Drop old column241 await queryInterface.removeColumn('users', 'address_string');242 },243244 down: async (queryInterface, Sequelize) => {245 // Reconstruct original column246 await queryInterface.addColumn('users', 'address_string', {247 type: Sequelize.STRING248 });249250 await queryInterface.sequelize.query(`251 UPDATE users252 SET address_string = CONCAT(street, ', ', city, ', ', state)253 `);254255 await queryInterface.removeColumn('users', 'street');256 await queryInterface.removeColumn('users', 'city');257 await queryInterface.removeColumn('users', 'state');258 }259};260```261262## Rollback Strategies263264### Transaction-Based Migrations265```javascript266module.exports = {267 up: async (queryInterface, Sequelize) => {268 const transaction = await queryInterface.sequelize.transaction();269270 try {271 await queryInterface.addColumn(272 'users',273 'verified',274 { type: Sequelize.BOOLEAN, defaultValue: false },275 { transaction }276 );277278 await queryInterface.sequelize.query(279 'UPDATE users SET verified = true WHERE email_verified_at IS NOT NULL',280 { transaction }281 );282283 await transaction.commit();284 } catch (error) {285 await transaction.rollback();286 throw error;287 }288 },289290 down: async (queryInterface) => {291 await queryInterface.removeColumn('users', 'verified');292 }293};294```295296### Checkpoint-Based Rollback297```javascript298module.exports = {299 up: async (queryInterface, Sequelize) => {300 // Create backup table301 await queryInterface.sequelize.query(302 'CREATE TABLE users_backup AS SELECT * FROM users'303 );304305 try {306 // Perform migration307 await queryInterface.addColumn('users', 'new_field', {308 type: Sequelize.STRING309 });310311 // Verify migration312 const [result] = await queryInterface.sequelize.query(313 "SELECT COUNT(*) as count FROM users WHERE new_field IS NULL"314 );315316 if (result[0].count > 0) {317 throw new Error('Migration verification failed');318 }319320 // Drop backup321 await queryInterface.dropTable('users_backup');322 } catch (error) {323 // Restore from backup324 await queryInterface.sequelize.query('DROP TABLE users');325 await queryInterface.sequelize.query(326 'CREATE TABLE users AS SELECT * FROM users_backup'327 );328 await queryInterface.dropTable('users_backup');329 throw error;330 }331 }332};333```334335## Zero-Downtime Migrations336337### Blue-Green Deployment Strategy338```javascript339// Phase 1: Make changes backward compatible340module.exports = {341 up: async (queryInterface, Sequelize) => {342 // Add new column (both old and new code can work)343 await queryInterface.addColumn('users', 'email_new', {344 type: Sequelize.STRING345 });346 }347};348349// Phase 2: Deploy code that writes to both columns350351// Phase 3: Backfill data352module.exports = {353 up: async (queryInterface) => {354 await queryInterface.sequelize.query(`355 UPDATE users356 SET email_new = email357 WHERE email_new IS NULL358 `);359 }360};361362// Phase 4: Deploy code that reads from new column363364// Phase 5: Remove old column365module.exports = {366 up: async (queryInterface) => {367 await queryInterface.removeColumn('users', 'email');368 }369};370```371372## Cross-Database Migrations373374### PostgreSQL to MySQL375```javascript376// Handle differences377module.exports = {378 up: async (queryInterface, Sequelize) => {379 const dialectName = queryInterface.sequelize.getDialect();380381 if (dialectName === 'mysql') {382 await queryInterface.createTable('users', {383 id: {384 type: Sequelize.INTEGER,385 primaryKey: true,386 autoIncrement: true387 },388 data: {389 type: Sequelize.JSON // MySQL JSON type390 }391 });392 } else if (dialectName === 'postgres') {393 await queryInterface.createTable('users', {394 id: {395 type: Sequelize.INTEGER,396 primaryKey: true,397 autoIncrement: true398 },399 data: {400 type: Sequelize.JSONB // PostgreSQL JSONB type401 }402 });403 }404 }405};406```407408## Resources409410- **references/orm-switching.md**: ORM migration guides411- **references/schema-migration.md**: Schema transformation patterns412- **references/data-transformation.md**: Data migration scripts413- **references/rollback-strategies.md**: Rollback procedures414- **assets/schema-migration-template.sql**: SQL migration templates415- **assets/data-migration-script.py**: Data migration utilities416- **scripts/test-migration.sh**: Migration testing script417418## Best Practices4194201. **Always Provide Rollback**: Every up() needs a down()4212. **Test Migrations**: Test on staging first4223. **Use Transactions**: Atomic migrations when possible4234. **Backup First**: Always backup before migration4245. **Small Changes**: Break into small, incremental steps4256. **Monitor**: Watch for errors during deployment4267. **Document**: Explain why and how4278. **Idempotent**: Migrations should be rerunnable428429## Common Pitfalls430431- Not testing rollback procedures432- Making breaking changes without downtime strategy433- Forgetting to handle NULL values434- Not considering index performance435- Ignoring foreign key constraints436- Migrating too much data at once437
Full transparency — inspect the skill content before installing.