Skip to content

Squash & Internals

After accumulating many migrations, you can squash them into a single baseline migration that captures the current schema as create_table + create_index operations.

When to squash:

  • Onboarding new team members — one file vs dozens of incremental changes
  • Cleaning up history — old add/drop/rename chains become noise
  • Speeding up fresh deployments — new environments apply one file instead of many
Terminal window
# Preview what would be squashed
alab squash --dry-run
# Squash all migrations into a baseline
alab squash
  1. Generates a baseline from your current schema
  2. Archives old migration files to .alab/archive/<timestamp>/
  3. Archives database history to migration_history.json in the same directory
  4. Writes a new 001_baseline.js
  5. Updates the alab_migrations table — clears old records, inserts a baseline record
  6. Regenerates the alab.lock file

Old files are preserved, not deleted:

.alab/archive/20250115_120000/
├── 001_initial_schema.js
├── 002_add_user_email.js
├── 003_add_blog_posts.js
└── migration_history.json

Existing databases are unaffected — the runner detects the baseline’s squashed_through revision has already been applied and skips it.

New databases apply only the baseline, then any migrations created after the squash.


When you run alab new, the generator follows a deterministic algorithm.

The generator compares your current schema files against the state stored in the last migration (not the database). It replays all existing migrations in-memory to reconstruct the schema, then diffs it against your schemas/ directory.

This means you don’t need a database connection to generate migrations — schema files remain the single source of truth.

Tables are created in dependency order using topological sort:

  1. Tables with no foreign key references first
  2. Then tables referencing only already-created tables
  3. Circular references are appended in alphabetical order

Columns appear in a predictable order: id first, then alphabetical, then created_at/updated_at last. This deterministic ordering means regenerating from the same schema always produces identical output.

The down() function is auto-generated as the reverse of up():

ForwardReverse
create_tabledrop_table
add_columndrop_column
rename_column(old, new)rename_column(new, old)
rename_table(old, new)rename_table(new, old)
create_indexdrop_index
add_foreign_keydrop_foreign_key
add_checkdrop_check
alter_columnalter_column (restores previous state)

Operations like drop_table, drop_column, and sql() cannot be auto-reversed — the generator marks these as irreversible and leaves comments in down() for manual handling.

Generated files are formatted with npx prettier when available.