Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/microck-database-migration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 135% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 81% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 81% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 253% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 128% | 0% |
Master database schema and data migrations across ORMs (Sequelize, TypeORM, Prisma), including rollback strategies and zero-downtime deployments.
javascript// migrations/20231201-create-users.js module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('users', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, email: { type: Sequelize.STRING, unique: true, allowNull: false }, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE }); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('users'); } }; // Run: npx sequelize-cli db:migrate // Rollback: npx sequelize-cli db:migrate:undo
typescript// migrations/1701234567-CreateUsers.ts import { MigrationInterface, QueryRunner, Table } from 'typeorm'; export class CreateUsers1701234567 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.createTable( new Table({ name: 'users', columns: [ { name: 'id', type: 'int', isPrimary: true, isGenerated: true, generationStrategy: 'increment' }, { name: 'email', type: 'varchar', isUnique: true }, { name: 'created_at', type: 'timestamp', default: 'CURRENT_TIMESTAMP' } ] }) ); } public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.dropTable('users'); } } // Run: npm run typeorm migration:run // Rollback: npm run typeorm migration:revert
prisma// schema.prisma model User { id Int @id @default(autoincrement()) email String @unique createdAt DateTime @default(now()) } // Generate migration: npx prisma migrate dev --name create_users // Apply: npx prisma migrate deploy
javascript// Safe migration: add column with default module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.addColumn('users', 'status', { type: Sequelize.STRING, defaultValue: 'active', allowNull: false }); }, down: async (queryInterface) => { await queryInterface.removeColumn('users', 'status'); } };
javascript// Step 1: Add new column module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.addColumn('users', 'full_name', { type: Sequelize.STRING }); // Copy data from old column await queryInterface.sequelize.query( 'UPDATE users SET full_name = name' ); }, down: async (queryInterface) => { await queryInterface.removeColumn('users', 'full_name'); } }; // Step 2: Update application to use new column // Step 3: Remove old column module.exports = { up: async (queryInterface) => { await queryInterface.removeColumn('users', 'name'); }, down: async (queryInterface, Sequelize) => { await queryInterface.addColumn('users', 'name', { type: Sequelize.STRING }); } };
javascriptmodule.exports = { up: async (queryInterface, Sequelize) => { // For large tables, use multi-step approach // 1. Add new column await queryInterface.addColumn('users', 'age_new', { type: Sequelize.INTEGER }); // 2. Copy and transform data await queryInterface.sequelize.query(` UPDATE users SET age_new = CAST(age AS INTEGER) WHERE age IS NOT NULL `); // 3. Drop old column await queryInterface.removeColumn('users', 'age'); // 4. Rename new column await queryInterface.renameColumn('users', 'age_new', 'age'); }, down: async (queryInterface, Sequelize) => { await queryInterface.changeColumn('users', 'age', { type: Sequelize.STRING }); } };
javascriptmodule.exports = { up: async (queryInterface, Sequelize) => { // Get all records const [users] = await queryInterface.sequelize.query( 'SELECT id, address_string FROM users' ); // Transform each record for (const user of users) { const addressParts = user.address_string.split(','); await queryInterface.sequelize.query( `UPDATE users SET street = :street, city = :city, state = :state WHERE id = :id`, { replacements: { id: user.id, street: addressParts[0]?.trim(), city: addressParts[1]?.trim(), state: addressParts[2]?.trim() } } ); } // Drop old column await queryInterface.removeColumn('users', 'address_string'); }, down: async (queryInterface, Sequelize) => { // Reconstruct original column await queryInterface.addColumn('users', 'address_string', { type: Sequelize.STRING }); await queryInterface.sequelize.query(` UPDATE users SET address_string = CONCAT(street, ', ', city, ', ', state) `); await queryInterface.removeColumn('users', 'street'); await queryInterface.removeColumn('users', 'city'); await queryInterface.removeColumn('users', 'state'); } };
javascriptmodule.exports = { up: async (queryInterface, Sequelize) => { const transaction = await queryInterface.sequelize.transaction(); try { await queryInterface.addColumn( 'users', 'verified', { type: Sequelize.BOOLEAN, defaultValue: false }, { transaction } ); await queryInterface.sequelize.query( 'UPDATE users SET verified = true WHERE email_verified_at IS NOT NULL', { transaction } ); await transaction.commit(); } catch (error) { await transaction.rollback(); throw error; } }, down: async (queryInterface) => { await queryInterface.removeColumn('users', 'verified'); } };
javascriptmodule.exports = { up: async (queryInterface, Sequelize) => { // Create backup table await queryInterface.sequelize.query( 'CREATE TABLE users_backup AS SELECT * FROM users' ); try { // Perform migration await queryInterface.addColumn('users', 'new_field', { type: Sequelize.STRING }); // Verify migration const [result] = await queryInterface.sequelize.query( "SELECT COUNT(*) as count FROM users WHERE new_field IS NULL" ); if (result[0].count > 0) { throw new Error('Migration verification failed'); } // Drop backup await queryInterface.dropTable('users_backup'); } catch (error) { // Restore from backup await queryInterface.sequelize.query('DROP TABLE users'); await queryInterface.sequelize.query( 'CREATE TABLE users AS SELECT * FROM users_backup' ); await queryInterface.dropTable('users_backup'); throw error; } } };
javascript// Phase 1: Make changes backward compatible module.exports = { up: async (queryInterface, Sequelize) => { // Add new column (both old and new code can work) await queryInterface.addColumn('users', 'email_new', { type: Sequelize.STRING }); } }; // Phase 2: Deploy code that writes to both columns // Phase 3: Backfill data module.exports = { up: async (queryInterface) => { await queryInterface.sequelize.query(` UPDATE users SET email_new = email WHERE email_new IS NULL `); } }; // Phase 4: Deploy code that reads from new column // Phase 5: Remove old column module.exports = { up: async (queryInterface) => { await queryInterface.removeColumn('users', 'email'); } };
javascript// Handle differences module.exports = { up: async (queryInterface, Sequelize) => { const dialectName = queryInterface.sequelize.getDialect(); if (dialectName === 'mysql') { await queryInterface.createTable('users', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, data: { type: Sequelize.JSON // MySQL JSON type } }); } else if (dialectName === 'postgres') { await queryInterface.createTable('users', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, data: { type: Sequelize.JSONB // PostgreSQL JSONB type } }); } } };
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 35,458 | 12,147 | -66% | 1 | 1 | 0% | 2,162 | 5,078 | +135% | 0 | 0 | — |
case-02 | fail→pass | 14,349 | 11,207 | -22% | 1 | 1 | 0% | 2,770 | 5,004 | +81% | 0 | 0 | — |
case-03 | pass→pass | 14,092 | 16,596 | +18% | 1 | 1 | 0% | 2,408 | 5,493 | +128% | 0 | 0 | — |
case-04 | pass→pass | 15,447 | 17,288 | +12% | 1 | 1 | 0% | 2,661 | 5,730 | +115% | 0 | 0 | — |
case-05 | fail→fail | 13,310 | 10,593 | -20% | 1 | 1 | 0% | 2,200 | 4,601 | +109% | 0 | 0 | — |
case-06 | pass→pass | 9,614 | 10,587 | +10% | 1 | 1 | 0% | 1,733 | 4,602 | +166% | 0 | 0 | — |
case-07 | pass→pass | 14,427 | 12,961 | -10% | 1 | 1 | 0% | 2,847 | 5,551 | +95% | 0 | 0 | — |
case-08 | pass→pass | 15,516 | 13,260 | -15% | 1 | 1 | 0% | 2,667 | 5,132 | +92% | 0 | 0 | — |
case-09 | fail→pass | 14,363 | 11,637 | -19% | 1 | 1 | 0% | 2,814 | 5,101 | +81% | 0 | 0 | — |
case-10 | pass→pass | 16,785 | 18,399 | +10% | 1 | 1 | 0% | 2,983 | 6,008 | +101% | 0 | 0 | — |
case-11 | pass→pass | 9,860 | 8,077 | -18% | 1 | 1 | 0% | 1,629 | 4,156 | +155% | 0 | 0 | — |
case-12 | pass→pass | 10,695 | 12,901 | +21% | 1 | 1 | 0% | 1,867 | 5,217 | +179% | 0 | 0 | — |
case-13 | fail→fail | 17,130 | 17,888 | +4% | 1 | 1 | 0% | 2,860 | 6,251 | +119% | 0 | 0 | — |
case-14 | fail→pass | 5,833 | 5,761 | -1% | 1 | 1 | 0% | 1,045 | 3,686 | +253% | 0 | 0 | — |
case-15 | pass→pass | 9,027 | 8,962 | -1% | 1 | 1 | 0% | 1,704 | 4,395 | +158% | 0 | 0 | — |
case-16 | pass→pass | 7,510 | 5,710 | -24% | 1 | 1 | 0% | 1,243 | 3,634 | +192% | 0 | 0 | — |
case-17 | pass→pass | 10,141 | 9,103 | -10% | 1 | 1 | 0% | 1,667 | 4,187 | +151% | 0 | 0 | — |
case-18 | pass→pass | 9,345 | 8,028 | -14% | 1 | 1 | 0% | 1,441 | 4,065 | +182% | 0 | 0 | — |
case-24 | pass→pass | 13,822 | 12,399 | -10% | 1 | 1 | 0% | 2,560 | 5,076 | +98% | 0 | 0 | — |
case-19 | pass→pass | 10,612 | 9,055 | -15% | 1 | 1 | 0% | 1,545 | 4,041 | +162% | 0 | 0 | — |
case-20 | pass→pass | 9,233 | 8,890 | -4% | 1 | 1 | 0% | 1,448 | 4,263 | +194% | 0 | 0 | — |
case-21 | pass→pass | 7,149 | 6,290 | -12% | 1 | 1 | 0% | 1,119 | 3,635 | +225% | 0 | 0 | — |
case-22 | pass→pass | 14,192 | 12,692 | -11% | 1 | 1 | 0% | 2,582 | 5,267 | +104% | 0 | 0 | — |
case-23 | pass→pass | 11,115 | 8,728 | -21% | 1 | 1 | 0% | 1,954 | 4,205 | +115% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted, and 23 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +17 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.