Schema Branching Tutorial¶
Safe schema evolution using CinchDB's branching system.
Problem → Solution¶
Problem: Schema changes risk breaking production apps and corrupting data
Solution: CinchDB branches isolate changes, enable testing, and provide rollback
When to Use¶
Change Type | Use Branch? | Reason |
---|---|---|
Add table/column | Yes | Test integration first |
Drop table/column | Yes | Avoid accidental data loss |
Rename column | Yes | Coordinate app updates |
Add index | Optional | Low risk, but good practice |
Quick Workflow¶
# 1. Create feature branch
cinch branch create feature.add-user-profiles
cinch branch switch feature.add-user-profiles
# 2. Make schema changes
cinch table create user_profiles user_id:TEXT bio:TEXT? website:TEXT? location:TEXT? avatar_url:TEXT?
cinch column add users profile_complete:BOOLEAN
# 3. Test changes
cinch insert user_profiles --data '{"user_id": "123", "bio": "Software developer"}'
cinch table info user_profiles
# 4. Deploy to production
cinch branch changes # Review
cinch branch merge-into-main
Migration Patterns¶
Safe Changes (No App Coordination Needed)¶
# Add new tables
cinch table create audit_logs event:TEXT user_id:TEXT data:TEXT?
# Add nullable columns
cinch column add users last_seen:TEXT?
# Add indexes
cinch index create users email --unique
Risky Changes (Coordinate with App)¶
# Renaming strategy - two-phase deployment
cinch branch create refactor.rename-columns
# Phase 1: Add new column, populate it
cinch column add users display_name:TEXT?
# Update data via your application code, not CLI
# Update app to use display_name, deploy app
# Phase 2: Drop old column (separate branch)
cinch branch create cleanup.remove-old-name-column
cinch column drop users name # Use column command, not raw SQL
Branch Testing¶
Pattern: Test schema changes before merging to catch issues early
import pytest
import cinchdb
def test_new_user_profiles_table():
# Test against feature branch
db = cinchdb.connect("myapp", branch="feature.add-user-profiles")
# Verify table exists
tables = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='user_profiles'")
assert len(tables) == 1
# Test CRUD operations
profile = db.insert("user_profiles", {"user_id": "test-123", "bio": "Test bio"})
assert profile["id"] is not None
# Test constraints
with pytest.raises(Exception):
db.insert("user_profiles", {"bio": "No user"}) # Missing required user_id
def validate_branch(branch_name: str):
"""Run full validation suite."""
db = cinchdb.connect("myapp", branch=branch_name)
# Schema validation
tables = db.query("SELECT name FROM sqlite_master WHERE type='table'")
assert len(tables) > 0
# Performance check
import time
start = time.time()
db.query("SELECT COUNT(*) FROM users")
assert (time.time() - start) < 1.0
# Data integrity
orphans = db.query("SELECT COUNT(*) as count FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.id IS NULL")
assert orphans[0]["count"] == 0
return True
Testing Checklist¶
Check | Command/Test |
---|---|
Schema valid | cinch table list |
Core queries work | cinch query "SELECT * FROM users LIMIT 1" |
Performance OK | Time critical queries |
No orphaned data | Check foreign key integrity |
App compatibility | Run app test suite against branch |
Merge Strategies¶
Fast-Forward (Simple)¶
Review Changes First¶
# Review what will be merged
cinch branch changes feature.risky-change
# Look for dangerous operations
grep -E "DROP|DELETE" changes.sql
# Merge if safe
cinch branch merge feature.risky-change main
Staged Rollout¶
def staged_rollout(branch_name: str):
"""Roll out schema changes gradually."""
db = cinchdb.connect("myapp")
# Stage 1: Test tenants
test_tenants = ["test_tenant_1", "test_tenant_2"]
for tenant in test_tenants:
test_db = cinchdb.connect(db.database, branch=branch_name, tenant=tenant)
# Run validation tests...
print(f"✓ Tested on {tenant}")
# Stage 2: Beta customers (after manual approval)
if input("Proceed to beta customers? (y/n): ") == 'y':
# Apply to beta tenants...
pass
# Stage 3: Full production rollout
if input("Deploy to all customers? (y/n): ") == 'y':
db.branches.merge_branches(branch_name, "main")
## Rollback Procedures
| Situation | Action |
|-----------|--------|
| Pre-merge | `cinch branch delete feature.bad-idea --force` |
| Post-merge | Create rollback branch with reverse changes |
| Emergency | Switch all apps back to previous main branch |
### Emergency Rollback
```bash
# Create rollback branch
cinch branch create rollback/emergency-$(date +%s)
# Reverse changes using appropriate commands
cinch table drop problematic_table
# Deploy rollback
cinch branch merge-into-main
Best Practices¶
Branch Naming Convention¶
feature.description
- New featuresbugfix.issue-123
- Bug fixeshotfix.urgent-fix
- Urgent production fixesrefactor.cleanup
- Code improvementsexperiment.idea
- Experimental changes
Keep Changes Small¶
# ✅ Good: Single purpose
cinch branch create feature.add-user-avatars
# ❌ Bad: Too many changes
cinch branch create feature.everything-update
Clean Up After Merge¶
# Remove completed branches
cinch branch delete feature.completed-feature
# List old branches
cinch branch list | grep feature.
Next Steps¶
- Apply branching to Multi-Tenant Apps
- Review Branching Concepts for advanced patterns