Skip to content

Database Migration

Safe database schema evolution for production apps with user data. Prevents data loss with additive, idempotent migration patterns.

When to Use This Skill

Use this skill when you're:

  • Adding new columns to existing tables
  • Modifying column types or constraints
  • Getting "FOREIGN KEY constraint failed" errors
  • Getting "no such column" errors
  • Getting "cannot add NOT NULL column" errors
  • Adding relationships between tables
  • Planning schema migrations before release

Core principle: Migrations are immutable after shipping. Make them additive and idempotent.

Example Prompts

Questions you can ask Claude that will draw from this skill:

  • "I need to add a new column but the app is live with user data. How do I do this safely?"
  • "I'm getting 'cannot add NOT NULL column' errors. What's wrong?"
  • "I need to change a column from text to integer. Can I just ALTER the column type?"
  • "How do I add a foreign key without breaking existing data?"
  • "Users are crashing after update. I changed a migration but it's already in production."
  • "How do I test migrations before shipping?"
  • "What's the safe pattern for renaming a column?"

What's Covered

Critical Rules (Data Loss Prevention)

NEVER do these with user data:

  • ❌ DROP TABLE
  • ❌ Modify shipped migrations (create new one)
  • ❌ Recreate tables to change schema
  • ❌ Add NOT NULL column without DEFAULT
  • ❌ Delete columns (SQLite limitation)

Safe Patterns

  • Adding nullable columns
  • Adding columns with defaults
  • Type migration (new column → migrate → deprecate)
  • Foreign key addition sequence
  • Index creation

Testing Workflows

  • Pre-flight migration testing
  • Rollback strategies
  • Version compatibility testing

Framework-Specific

  • GRDB DatabaseMigrator patterns
  • SQLiteData @Column migrations
  • SwiftData schema versioning

Key Pattern

Adding a Column Safely

swift
// ✅ SAFE: Add nullable column (works with existing rows)
migrator.registerMigration("v2_add_preferences") { db in
    try db.execute(sql: """
        ALTER TABLE users ADD COLUMN preferences TEXT
    """)
}

// ✅ SAFE: Add column with default (works with existing rows)
migrator.registerMigration("v3_add_created_at") { db in
    try db.execute(sql: """
        ALTER TABLE users ADD COLUMN created_at TEXT DEFAULT ''
    """)
}

// ❌ UNSAFE: NOT NULL without default (fails on existing rows)
try db.execute(sql: """
    ALTER TABLE users ADD COLUMN required_field TEXT NOT NULL
    -- Error: cannot add NOT NULL column with default value NULL
""")

Changing Column Type Safely

swift
// ✅ SAFE: Add new → migrate → keep old (never delete)
migrator.registerMigration("v4_migrate_age_to_int") { db in
    // 1. Add new column
    try db.execute(sql: "ALTER TABLE users ADD COLUMN age_int INTEGER")

    // 2. Migrate data
    try db.execute(sql: "UPDATE users SET age_int = CAST(age AS INTEGER)")

    // 3. Keep old column (for backwards compatibility)
    // NEVER: DROP COLUMN age
}

Documentation Scope

This page documents the axiom-database-migration skill—safe schema evolution patterns Claude uses when helping you plan and execute migrations. The skill contains complete patterns for SQLite, GRDB, SQLiteData, and SwiftData.

For persistence frameworks: See swiftdata, grdb, or sqlitedata for framework-specific guidance.

  • swiftdata — SwiftData patterns including schema versioning
  • grdb — GRDB DatabaseMigrator patterns
  • sqlitedata — SQLiteData @Column and migration patterns
  • xcode-debugging — Environment diagnostics when migrations fail

Resources

WWDC: 2019-230, 2023-10187

Docs: /grdb, /swiftdata/schema-versioning

Released under the MIT License