Schema Migrations
On this page
- Overview
- Creating Migrations
- Using the CLI (Future)
- Manual Creation (Current)
- Migration File Format
- Applying Migrations
- Automatic (Development)
- Manual (Current)
- Migration Naming Convention
- Best Practices
- 1. One Migration Per Logical Change
- 2. Always Add, Never Modify
- 3. Test Migrations on Copy of Production Data
- 4. Include Rollback Logic (Optional)
- 5. Use Transactions (Future)
- Migration State Tracking
- Common Patterns
- Adding a Table
- Adding a Column
- Adding an Index
- Adding Constraints
- Data Migrations
- Multi-Tenant Migrations
- Handling Failures
- Version Control
- Example: Full Migration Workflow
- Troubleshooting
- “Migration already applied”
- “Checksum mismatch”
- “Migration failed: syntax error”
- “Table already exists”
- Related Documentation
How to evolve your database schema over time in Kimberlite.
Overview
Kimberlite uses SQL-based migrations to version and apply schema changes. Migrations are:
- Sequential: Applied in order (001, 002, 003, …)
- Immutable: Once applied, cannot be changed
- Tracked: System records which migrations have been applied
- Reversible: Can include rollback logic (optional)
Creating Migrations
Using the CLI (Future)
Once the kmb CLI is available (v0.6.0):
# Create a new migration
# This creates: migrations/0001_add_appointments_table.sql
Manual Creation (Current)
For now, create migration files manually:
# Create migrations directory
# Create first migration
Migration File Format
Migration files are SQL scripts:
-- migrations/0001_initial_schema.sql
-- Create patients table
(
id BIGINT PRIMARY KEY,
name TEXT NOT NULL,
date_of_birth DATE,
medical_record_number TEXT UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create index for faster lookups
ON patients(date_of_birth);
-- Create appointments table
(
id BIGINT PRIMARY KEY,
patient_id BIGINT NOT NULL,
appointment_date TIMESTAMP NOT NULL,
provider TEXT,
status TEXT DEFAULT 'scheduled',
notes TEXT
);
ON appointments(patient_id);
ON appointments(appointment_date);
Applying Migrations
Automatic (Development)
The development server applies migrations automatically:
# Future: kmb dev
# Automatically applies pending migrations on startup
Manual (Current)
For now, apply migrations manually:
use Client;
Migration Naming Convention
Use descriptive names with sequence numbers:
migrations/
├── 0001_initial_schema.sql
├── 0002_add_billing_tables.sql
├── 0003_add_audit_triggers.sql
├── 0004_add_patient_consents.sql
└── 0005_add_encryption_keys.sql
Pattern: {sequence}_{description}.sql
- Sequence: 4-digit number (0001, 0002, …)
- Description: snake_case, descriptive
- Extension:
.sql
Best Practices
1. One Migration Per Logical Change
# Good: One migration for related changes
# Bad: Multiple unrelated changes
2. Always Add, Never Modify
-- Good: New migration to add column
-- migrations/0002_add_email_to_patients.sql
patients ADD COLUMN email TEXT;
-- Bad: Editing 0001 to add email (breaks existing deployments)
3. Test Migrations on Copy of Production Data
# 1. Export production data
# 2. Import to test environment
# 3. Apply migration
# 4. Verify
4. Include Rollback Logic (Optional)
-- migrations/0002_add_email_to_patients.sql
-- UP: Apply changes
patients ADD COLUMN email TEXT;
-- DOWN: Rollback changes (commented out, for reference)
-- ALTER TABLE patients DROP COLUMN email;
5. Use Transactions (Future)
Once supported:
BEGIN;
-- Migration changes
patients ADD COLUMN email TEXT;
ON patients(email);
COMMIT;
Migration State Tracking
Kimberlite tracks applied migrations in a system table:
-- System table (automatically created)
(
id BIGINT PRIMARY KEY,
version TEXT NOT NULL,
name TEXT NOT NULL,
applied_at TIMESTAMP NOT NULL,
checksum TEXT NOT NULL
);
Query migration status:
-- See which migrations have been applied
SELECT * FROM __migrations ORDER BY id;
-- Check if specific migration applied
SELECT * FROM __migrations WHERE name = '0001_initial_schema';
Common Patterns
Adding a Table
-- migrations/0003_add_prescriptions.sql
(
id BIGINT PRIMARY KEY,
patient_id BIGINT NOT NULL,
medication TEXT NOT NULL,
dosage TEXT,
prescribed_date TIMESTAMP NOT NULL,
prescriber TEXT NOT NULL
);
ON prescriptions(patient_id);
Adding a Column
-- migrations/0004_add_phone_to_patients.sql
patients ADD COLUMN phone TEXT;
Adding an Index
-- migrations/0005_add_name_index.sql
ON patients(name);
Adding Constraints
-- migrations/0006_add_patient_constraints.sql
appointments
ADD CONSTRAINT appointments_patient_fk
FOREIGN KEY (patient_id) REFERENCES patients(id);
Data Migrations
For data transformations:
-- migrations/0007_normalize_phone_numbers.sql
-- Update existing phone numbers to normalized format
UPDATE patients
SET phone = REPLACE(REPLACE(REPLACE(phone, '-', ''), '(', ''), ')', '')
WHERE phone IS NOT NULL;
Warning: Data migrations can be slow on large tables. Consider:
- Running during off-peak hours
- Batching updates
- Using background jobs
Multi-Tenant Migrations
Migrations apply to all tenants:
-- This migration applies to ALL tenants
(
id BIGINT PRIMARY KEY,
patient_id BIGINT NOT NULL,
medication TEXT NOT NULL
);
Per-tenant differences are handled via tenant configuration, not separate schemas.
Handling Failures
If a migration fails:
- Check the error:
kmb migration status - Fix the issue: Correct the SQL
- Mark as failed:
kmb migration mark-failed 0003 - Reapply:
kmb migration apply 0003_fixed.sql
Never modify a migration after it’s been applied to production.
Version Control
Commit migrations to version control:
All environments (dev, staging, production) should have identical migration history.
Example: Full Migration Workflow
# 1. Create migration
# 2. Write SQL
# 3. Test locally
# 4. Verify
# 5. Commit
# 6. Deploy to staging
# (CI/CD applies migrations automatically)
# 7. Deploy to production
# (CI/CD applies migrations automatically)
Troubleshooting
“Migration already applied”
Cause: Trying to apply a migration that’s already in __migrations.
Solution: Check status with SELECT * FROM __migrations.
“Checksum mismatch”
Cause: Migration file changed after being applied.
Solution: Never modify applied migrations. Create a new migration instead.
“Migration failed: syntax error”
Cause: Invalid SQL syntax.
Solution: Test migration in development first.
“Table already exists”
Cause: Schema already has the table (possibly from manual changes).
Solution: Use CREATE TABLE IF NOT EXISTS or reconcile schema manually.
Related Documentation
- First Application - Building your first app
- Testing Guide - Testing migrations
- Connection Pooling - Database connections
Status: Current implementation is manual. Full migration tooling coming in v0.6.0.