Overview
What is Clojure Migratus By Example?
Clojure Migratus By Example is a code-first tutorial series teaching experienced Clojure developers how to manage database schema evolution using Migratus. Through heavily annotated, self-contained examples, you will achieve 40% coverage of Migratus patterns at the beginner level—from configuration and SQL migration files to running, rolling back, and inspecting pending migrations.
This tutorial assumes you are an experienced developer familiar with Clojure, deps.edn, next.jdbc, and relational databases. If you are new to Clojure, start with foundational Clojure tutorials first.
Why By Example?
Philosophy: Show the code first, run it second, understand through direct interaction.
Traditional tutorials explain concepts then show code. By-example tutorials reverse this: every example is a working, runnable code snippet with inline annotations showing exactly what happens at each step—config maps, SQL file contents, migration states, and REPL output.
Target Audience: Experienced developers who:
- Already know Clojure fundamentals (namespaces, maps, deps.edn)
- Understand relational databases and SQL DDL
- Prefer learning through working code rather than narrative explanations
- Want comprehensive reference material covering production migration patterns
Not For: Developers new to Clojure or SQL. This tutorial moves quickly and assumes foundational knowledge.
What Does Coverage Mean?
Coverage means the depth and breadth of Migratus features needed for production work, not toy examples.
Included in Beginner Coverage (0-40%)
- Configuration: Config map structure, :store :database, :migration-dir, JDBC URIs
- Migration Files: Naming conventions, up/down SQL pairs, file placement
- Core Operations: migrate, up, down, create, pending-list
- Common DDL Patterns: CREATE TABLE, ADD COLUMN, ADD INDEX, foreign keys, constraints
- Schema Tracking: schema_migrations table structure and behavior
- Data Seeding: INSERT statements in migration files
- Safety Guards: IF NOT EXISTS, IF EXISTS, cascade behavior
- deps.edn Integration: Declaring the Migratus dependency
Excluded from Beginner Coverage
- Multiple Stores: In-memory, filesystem stores (rarely used in production)
- Custom Reporters: Progress callbacks and custom logging hooks
- Advanced Transactions: Per-migration transaction control flags
- Migration Scripting: Clojure-based (non-SQL) migration files
- Programmatic Discovery: Dynamic migration directory resolution
Tutorial Structure
Examples Across One Level
Beginner (Examples 1-30): 0-40% coverage
- Configuration and file naming (Examples 1-5)
- Core API operations (Examples 6-10)
- Table and column DDL (Examples 11-16)
- Connection and type patterns (Examples 17-23)
- Structural patterns and safety (Examples 24-30)
Five-Part Example Format
Every example follows a mandatory five-part structure:
Part 1: Brief Explanation (2-3 sentences)
Answers what the concept is, why it matters in production code, and when to use it.
Part 2: Mermaid Diagram (when appropriate)
Included when data flow or relationships are non-obvious. Uses the color-blind-friendly palette:
- Blue
#0173B2, Orange#DE8F05, Teal#029E73, Purple#CC78BC, Brown#CA9161
Part 3: Heavily Annotated Code
Every significant line has an inline comment. Clojure annotations use ; => notation. SQL annotations use -- => notation.
(def config
{:store :database ; => Use the database store (SQL-based)
:migration-dir "migrations" ; => Relative to classpath root (resources/)
:db {:connection-uri uri}}) ; => next.jdbc-compatible JDBC URI mapPart 4: Key Takeaway (1-2 sentences)
Distills the core insight and when to apply it in production.
Part 5: Why It Matters (50-100 words)
Explains the production relevance, common pitfalls, and consequences of ignoring the pattern.
Self-Containment Rules
Each example must be copy-paste-runnable within its chapter scope:
- Full config map or SQL file content shown
- No references to code outside the example
- Clojure REPL snippets include all required
requirecalls - SQL files shown in full (no ellipsis)
Code Annotation Philosophy
Every example uses educational annotations to show exactly what happens:
(migratus/migrate config) ; => Runs all pending migrations in order
; => Reads files from resources/migrations/
; => SQL: SELECT id FROM schema_migrations
; => Output: Migrating 001-create-usersAnnotations show:
- Config values and what each key controls
- File system effects (which files are read/created)
- SQL executed by Migratus internally
- Return values and REPL output
- Common gotchas and sequencing constraints
Quality Standards
Every example in this tutorial meets these standards:
- Self-contained: Copy-paste-runnable within chapter scope
- Annotated: Every significant line has an inline comment (1.0-2.25 ratio per example)
- Production-relevant: Real-world patterns based on actual Clojure project usage
- Accessible: Color-blind-friendly diagrams, clear structure
Next Steps
Ready to start? Begin with Beginner Examples (1-30) to learn Migratus from configuration through advanced DDL patterns.
Examples by Level
Beginner (Examples 1–30)
- Example 1: Migratus Config Map
- Example 2: First Migration Pair (up.sql / down.sql)
- Example 3: Migration File Naming Convention
- Example 4: :store :database Configuration
- Example 5: :migration-dir Setting
- Example 6: Running All Migrations (migratus/migrate)
- Example 7: Running Single Migration Up (migratus/up)
- Example 8: Rolling Back Single Migration (migratus/down)
- Example 9: Creating New Migration (migratus/create)
- Example 10: Checking Pending Migrations (migratus/pending-list)
- Example 11: Creating Tables
- Example 12: Adding Columns
- Example 13: Adding Indexes
- Example 14: Adding Foreign Keys
- Example 15: Adding Unique Constraints
- Example 16: schema_migrations Table Structure
- Example 17: JDBC Connection String Setup
- Example 18: NOT NULL with Default Values
- Example 19: UUID Primary Keys (PostgreSQL)
- Example 20: Timestamp Columns with Defaults
- Example 21: Enum Types via SQL
- Example 22: CHECK Constraints
- Example 23: Composite Indexes
- Example 24: Junction Tables (Many-to-Many)
- Example 25: Seed Data in Migrations
- Example 26: Multiple Statements in One File
- Example 27: IF NOT EXISTS Guards
- Example 28: Cascade Delete Foreign Keys
- Example 29: Dropping Tables/Columns Safely
- Example 30: deps.edn Dependency Declaration
Intermediate (Examples 31–60)
- Example 31: Clojure-Based Migrations (.clj Files)
- Example 32: Transaction Wrapping in Migrations
- Example 33: Rollback Command (migratus/rollback)
- Example 34: Reset Command (migratus/reset)
- Example 35: Pending Migrations List
- Example 36: Custom Migration Store
- Example 37: Data Migration with INSERT...SELECT
- Example 38: Seed Data Pattern
- Example 39: Foreign Key with ON UPDATE CASCADE
- Example 40: Composite Primary Keys
- Example 41: Partial Indexes
- Example 42: Full-Text Search Indexes
- Example 43: Creating Views
- Example 44: Creating Materialized Views
- Example 45: Trigger Functions
- Example 46: Stored Procedures
- Example 47: Conditional Migration Logic
- Example 48: Batch Data Migration Pattern
- Example 49: Migration Testing with clojure.test
- Example 50: Test Database Setup with Testcontainers
- Example 51: JSON/JSONB Columns
- Example 52: Array Columns (PostgreSQL)
- Example 53: GIN Index for JSONB
- Example 54: Table Partitioning
- Example 55: Generated Columns
- Example 56: Connection Pooling with HikariCP
- Example 57: Migration with next.jdbc
- Example 58: Multi-Database Support
- Example 59: Migration Error Handling
- Example 60: Pedestal Integration Pattern
Advanced (Examples 61–85)
- Example 61: Custom Migration Protocol
- Example 62: Zero-Downtime Column Addition
- Example 63: Zero-Downtime Column Removal (3-Phase)
- Example 64: Zero-Downtime Table Rename
- Example 65: Large Table Migration with Batched Updates
- Example 66: Online Index Creation (CONCURRENTLY)
- Example 67: Data Backfill Pattern
- Example 68: Migratus in CI/CD Pipeline
- Example 69: Migration Monitoring with Metrics
- Example 70: Migration Rollback Testing
- Example 71: Blue-Green Deployment Migrations
- Example 72: Feature Flag Migration Pattern
- Example 73: Multi-Tenant Schema Migration
- Example 74: Migration with pgcrypto Encryption
- Example 75: Audit Trail Table Migration
- Example 76: Soft Delete Schema Pattern
- Example 77: REPL-Driven Migration Development
- Example 78: Migration with HugSQL Integration
- Example 79: Schema Drift Detection
- Example 80: Migration Dependency Graph
- Example 81: Migration Squashing Pattern
- Example 82: Migration Performance Benchmarking
- Example 83: Stored Procedures in Advanced Patterns
- Example 84: Production Migration Checklist
- Example 85: Migration Observability and Alerting
Last updated March 26, 2026