sqlmesh-migrations
Authors and runs SQLMesh - data-transformation framework with version control, virtual data environments, automatic breaking-vs-non-breaking change classification, and downstream impact analysis; supports `sqlmesh init` / `plan` / `apply` / `run` / `audit` / `test` lifecycle; covers DuckDB, Postgres, Snowflake, BigQuery, Redshift, Databricks. Use when the user works with SQL data pipelines (warehouse + dbt-adjacent ELT), needs safer model evolution than dbt's deploy-and-pray, or wants the strongest impact-analysis story in the OSS data tooling space.
Install with skills.sh (any agent)
npx skills add testland/qa --skill sqlmesh-migrationssqlmesh-migrations
Overview
Per sqlmesh.readthedocs.io/en/stable/quickstart/cli/ (opens in new window):
SQLMesh is a data transformation tool that enables version control and testing for SQL pipelines. Its distinguishing features vs schema-only tools (Flyway / Liquibase / Atlas):
When to use
Step 1 - Install
Per sm-cli (opens in new window):
pip install sqlmeshOptionally with extras for specific engines (e.g., pip install sqlmesh[bigquery]).
Step 2 - Initialize a project
Per sm-cli (opens in new window):
sqlmesh init <dialect>
# example:
sqlmesh init duckdbGenerates project skeleton: config.yaml, models/, macros/, tests/, audits/, seeds/.
Step 3 - Author a model
A SQLMesh model is a SQL (or Python) file in models/ with a MODEL directive header:
-- models/sales/orders_summary.sql
MODEL (
name sales.orders_summary,
kind FULL,
cron '@daily',
owner 'data-team@example.com',
description 'Daily summary of orders by customer'
);
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM sales.orders
GROUP BY customer_id;Model kinds (per SQLMesh docs): FULL (rebuild every run), INCREMENTAL_BY_TIME_RANGE (process new time partitions), INCREMENTAL_BY_UNIQUE_KEY (upsert by key), VIEW (no persisted table), SEED (static data).
Step 4 - Plan + apply (the core workflow)
Per sm-cli (opens in new window):
sqlmesh plan devplan shows:
The user reviews the plan, then confirms - plan is integrated with apply: confirming the plan applies it.
sqlmesh plan prod # promote dev to prodThe promotion is virtual until apply: prod continues serving existing data until the new env is built.
Step 5 - Run scheduled execution
sqlmesh runRuns models per their cron schedule. Typically scheduled in CI/CD (daily / hourly), sqlmesh run checks each model and executes if its cron is due.
Step 6 - Audits
Audits are SQL-based data-quality checks attached to models:
-- audits/no_null_amounts.sql
AUDIT (
name no_null_amounts,
);
SELECT * FROM @this_model WHERE amount IS NULL;sqlmesh auditReturns failures if the audit query returns any rows.
Compare with great-expectations and soda-checks (in the qa-data-quality plugin): SQLMesh audits are tightly coupled to SQLMesh models; GE/Soda are standalone data-quality frameworks. Choose audits when you're already in SQLMesh; GE/Soda for cross-framework data quality.
Step 7 - Tests (unit tests on models)
Unlike audits (which run on real data), tests run on synthetic input → synthetic output:
# tests/test_orders_summary.yaml
test_orders_summary:
model: sales.orders_summary
inputs:
sales.orders:
- { customer_id: 1, amount: 100.00 }
- { customer_id: 1, amount: 200.00 }
- { customer_id: 2, amount: 50.00 }
outputs:
query:
- { customer_id: 1, order_count: 2, total_amount: 300.00 }
- { customer_id: 2, order_count: 1, total_amount: 50.00 }sqlmesh testStep 8 - CI integration
- run: pip install sqlmesh
- run: sqlmesh test # unit tests
- run: sqlmesh plan ci-${{ github.run_id }} --no-prompts # build a per-PR env
- run: sqlmesh audit # data-quality checks
# Promotion to prod is a separate workflow with manual approval gateThe per-PR env approach gives full data-pipeline isolation: each PR materializes its own copy of the models, audits run against real PR data, and merge-then-promote is the production path.
Step 9 - Composition with sister tools
Beyond sqlmesh plan's built-in breaking/non-breaking classification, apply adversarial review of breaking changes that estimates downstream consumer impact (BI dashboards, downstream services) that SQLMesh's model graph alone doesn't capture.
For underlying schema migrations (DDL on the warehouse, separate from SQLMesh model changes), use flyway-migrations or atlas-migrations.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
sqlmesh apply directly to prod without dev plan | No review of breaking-vs-non-breaking; broken pipelines | Always plan dev first (Step 4) |
Use kind FULL for everything | Full rebuilds expensive on large tables | Pick INCREMENTAL_BY_TIME_RANGE / BY_UNIQUE_KEY per model semantics |
| Skip audits on critical models | Data drift goes undetected | Audit every transformation (Step 6) |
| Write tests against production data instead of synthetic | Tests pass-by-accident; brittle | Use the inputs/outputs test format (Step 7) |
| Treat SQLMesh as schema-migration tool | SQLMesh manages data models, not raw DDL | Pair with Flyway/Atlas for raw schema (Step 9) |
Limitations
References
Related skills
atlas-migrations
Authors and runs Atlas database schema migrations - declarative HCL or SQL schema definition with `atlas schema apply` for desired-state apply OR `atlas migrate diff` to generate versioned migrations against a dev DB; `atlas migrate apply` to deploy; `atlas migrate lint` to flag destructive / locking / data-loss patterns; `atlas migrate hash` to detect tampering. Supports PostgreSQL, MySQL, SQL Server, ClickHouse, SQLite, MariaDB, Snowflake, Oracle, Redshift, Spanner, CockroachDB, Databricks. Use when the user wants Terraform-style declarative DB schema management or modern SQL-first migration linting beyond Flyway / Liquibase.
flyway-migrations
Authors and runs Flyway database migrations - versioned (`V1__add_users.sql`), repeatable (`R__refresh_views.sql`), and undo (`U1__remove_users.sql`) migration files in `db/migration/`; runs `flyway migrate` / `info` / `validate` / `clean` / `baseline` / `repair`; tracks state in the `flyway_schema_history` table; supports 50+ databases including Oracle / SQL Server / MySQL / PostgreSQL / MariaDB / Snowflake / BigQuery; integrates with Maven, Gradle, CLI, and Docker. Use when the user works with Flyway-managed schemas, asks about migration ordering, or needs CI gates on schema changes.
liquibase-migrations
Authors and runs Liquibase database migrations - changelog-driven schema management with changesets in XML / YAML / JSON / SQL formats; supports `liquibase update` / `status` / `rollback` / `tag` / `history` lifecycle; offers per-changeset preconditions, contexts and labels for selective execution, and rollback semantics; tracks state in `DATABASECHANGELOG` + `DATABASECHANGELOGLOCK` tables. Use when the user works with Liquibase-managed schemas (Spring Boot heritage, polyglot DB shops), needs cross-DBMS portable migrations, or requires fine-grained rollback control.
migration-operation-taxonomy
Classifies every DDL and DML statement in a database migration into an eight-category operation taxonomy (additive, backwards-compatible alter, locking, lock-escalating, breaking, data-loss, unsafe default, index-missing foreign key) and assigns a Critical, Warning, or Info severity justified by the lock mode and table-rewrite behavior the target engine actually performs. Records where PostgreSQL and MySQL/InnoDB diverge for the same logical statement, and where behavior is version-gated (PostgreSQL 11 removed the table rewrite for a constant DEFAULT; MySQL 8.0.12 made ADD COLUMN instant). Use when a migration file appears in a diff or a review queue and someone must decide, before it reaches a production-sized table, which statements are safe and which will stall writes or destroy data.