DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
FREEDOM251

Freedom251 › Blog › database CI

How Schema and Migration Linters Protect Database Changes

By Freedom251 Team · · 13 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Schema linting checks whether the complete database design is internally coherent; migration linting checks whether a proposed SQL change is safe to execute. The first can find cross-table defects such as incompatible foreign-key types, contradictory nullability and uniqueness rules, or missing required objects. The second can flag destructive statements, backward-incompatible changes, long locks, table rewrites, unsafe transaction behavior, and organization-specific policy violations before deployment. Neither subsumes the other: a safe-looking migration can leave a broken model, while a well-designed model can be damaged by a hazardous migration. Production teams should run schema linting against the assembled target schema and migration linting against every proposed migration, then apply the change in isolation and verify the resulting schema and rollback behavior.

The distinction is easiest to remember as scope: a schema linter reads the model that exists (or the model you intend to exist), while a migration linter reads the change you are about to run.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What is the difference between schema linting and migration linting?

Schema linting evaluates a complete schema or database model at a point in time. Migration linting evaluates one migration file, a migration directory, or a proposed change set in the context of the target engine and migration history. Their inspection units, timing, and failure costs are therefore different.

Comparison Schema linting Migration linting
Inspection unit All relevant tables, columns, keys, constraints, indexes, views, and other model objects One migration, a sequence of migration files, or the statements generated for a change
When it runs Against an existing database, a declarative schema, or an assembled target state Before the migration is executed, usually during local development and pull-request CI
Typical findings Incompatible key types, broken relationships, conflicting nullability or uniqueness, naming violations, and missing required objects Destructive drops, backward-incompatible changes, table locks, table rewrites, transaction hazards, risky backfills, and policy violations
Context required The whole set of objects that participate in a rule Migration text plus target-engine metadata, prior migration history, and sometimes table-size or lock information
Primary failure cost A latent correctness or maintainability defect that may surface later Deployment outage, blocked traffic, an unexpectedly long rewrite, incompatibility with running application versions, or data loss

Son Tran, a software engineer at Schemity, summarized the boundary on August 13, 2026: “A migration linter reads the statement you are about to run. A schema linter reads the model you already have.” That is a useful operational rule, not a claim that every product implements exactly the same checks.

What problems can schema linting see?

Schema linting answers, “Is this database design internally coherent?” Because it can inspect multiple objects together, it catches defects that cannot be proven from one SQL statement in isolation.

Are referenced and referencing key types compatible?

A foreign key is a relationship between two columns, so the linter must inspect both sides. It can detect, for example, a parent key stored as a 64-bit integer while a child key uses a narrower or incompatible type, or a text key that is compared with a numeric key. Depending on the database engine, such a mismatch may be rejected immediately, coerced unexpectedly, or create performance and data-integrity problems. The important point is that the defect belongs to the assembled model, not to one CREATE TABLE statement viewed alone.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do relationships and constraints describe the same business rule?

Whole-schema checks can find a relationship that points at a non-unique column, a required parent object that is absent, or a foreign key whose delete and update behavior conflicts with the rest of the model. They can also expose cycles and dependency chains that make ordering, deletion, or restoration difficult. These findings require the linter to understand the graph of tables and constraints.

Do nullability, uniqueness, and indexes work together?

A column may be declared nullable in one place while application-critical uniqueness assumes a value is always present. Composite unique constraints can also behave differently when one component is null, depending on the engine. A schema linter can review those interactions, check that required indexes or unique keys exist, and identify a design that technically parses but does not enforce the intended invariant.

Are required objects and conventions present?

Teams commonly encode rules for naming, primary keys, timestamps, ownership columns, audit fields, schemas, extensions, or approved data types. Schema linting can verify those rules across the model and identify omissions in a newly assembled schema. It is also the right place to check that views, sequences, generated columns, or other supporting objects expected by the application are present.

What does schema linting not prove?

A clean schema result does not prove that a deployment will be quick or non-blocking. It says little about how an ALTER TABLE will behave on a large, busy table, whether an old application version can continue writing, or whether a data backfill will finish inside the release window. Those are migration-level questions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What database hazards can migration linting catch?

Migration linting answers, “Is this change safe to apply to the target database?” It analyzes the proposed SQL and its context before execution, when the team can still rewrite the change or schedule it differently.

Will the migration destroy data or structure?

Destructive operations include dropping a table, column, index, or constraint; truncating data; or replacing an object in a way that loses dependent state. A migration linter can flag these statements for explicit review or reject them under a no-destructive-change policy. A drop is not automatically wrong, but it should be deliberate, with evidence that reads and writes no longer depend on the object and that retention requirements are satisfied.

Does the change break a running application?

Breaking changes include removing a column that an older application version still selects, changing a type or constraint so existing writes fail, or making a previously optional field mandatory before all writers have been updated. Migration linting can identify many of these patterns and enforce an expand-and-contract rollout: add a compatible structure, deploy code that uses it, backfill or dual-write, switch reads, and remove the old structure only after the compatibility window closes.

Could the operation lock a table or rewrite every row?

Some DDL statements acquire strong locks, scan an entire table, rebuild an index, or rewrite every row. The exact behavior depends on the database engine, version, table size, indexes, and concurrent workload. Atlas documents atlas migrate lint as checking migration files for table locks and rewrites in addition to destructive and breaking changes. A warning should trigger an execution-plan review: estimate affected rows, identify lock duration, test on production-like data, and decide whether an online or phased alternative is available.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Are transaction and backfill behaviors safe?

A migration can be syntactically valid yet unsafe because one transaction holds locks while a long backfill runs, because the engine disallows a particular DDL statement inside a transaction, or because retries would apply a non-idempotent data change twice. Migration rules can require explicit batching, a separate backfill job, a statement timeout, or a documented transaction boundary. These checks are specific to the engine and the organization’s operating policy.

Can custom policy rules be enforced?

Teams may prohibit unqualified deletes, require concurrent index creation, ban table rewrites in peak hours, or require an issue reference for destructive operations. Atlas describes custom rules as part of its migration-linting model. Such policy checks complement, rather than replace, the engine-aware analysis of locks and compatibility.

Can a migration linter catch a foreign-key mismatch?

Usually not reliably when the mismatch already exists elsewhere in the model. A migration linter sees the statements in the proposed change and whatever metadata the tool loads; it may notice an obvious error in a statement that creates a foreign key, but a single-statement view cannot reason about every related table, inherited object, or declarative file.

For example, a migration that adds orders.customer_id may look reasonable by itself. Only a whole-schema check can confirm that customers.id has the compatible type, the required uniqueness, the expected collation, and the intended delete behavior. Run schema linting on the target schema after assembling the migration result, and treat migration linting as the separate safety gate for executing the change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How can a migration be operationally safe yet leave a flawed schema?

Suppose a migration adds a nullable text column and completes quickly without blocking traffic. Migration linting may correctly approve its operational characteristics. Schema linting can still reject the resulting model if the column should be a foreign key, if its type conflicts with the referenced key, or if a required uniqueness rule is absent. The migration succeeded, but the design is wrong.

The reverse also occurs. A schema may satisfy every relationship and naming rule, yet a migration that drops an index, rewrites a billion-row table, or tightens nullability before the application is ready can cause an outage. A model check cannot predict all execution hazards from the final state alone. The two linters protect different dimensions of risk.

Why do schema files, migration history, and the live database need a baseline?

Lint results are only as trustworthy as the state being linted. A declarative schema file, the ordered migration history in version control, and the live database can diverge; each is a different description of reality.

How does a migration system derive a target state?

JetBrains describes migration as transferring structure and changes from an origin object to a target object. Its comparison view identifies objects that are new, modified, or deleted and generates statements intended to make the target equal the origin. That workflow is useful for producing a change, but the generated SQL still needs migration-level review for locks, rewrites, compatibility, and data effects.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What happens when someone edits the database directly?

Supabase documents that declarative schema synchronization compares schema files with migration history, not with every direct change in the live database. Edits made through Studio, a SQL editor, or psql are not automatically represented in that diff. Because schema migrations are versioned SQL statements, bypassing the history can cause db push synchronization errors when the recorded baseline no longer matches the remote database.

How should drift be repaired?

  1. Record the live database state with a schema dump and identify every object changed outside version control.
  2. Decide whether each difference is intentional. Keep approved changes, or revert accidental edits before generating new migrations.
  3. Bring the declarative schema and migration history into one agreed baseline; do not silently edit an old migration that has already run in shared environments.
  4. Rebuild an isolated database from the baseline, apply all migrations, and compare its schema dump with the approved target.

Linting a stale file set can produce a reassuring but irrelevant result, so baseline reconciliation belongs before the normal pull-request checks.

Should both linters run in continuous integration?

Yes. A production pull request should be checked against the target model and against the path used to reach it. The following sequence keeps those concerns separate while still testing the complete change.

  1. Assemble the target schema. Start from the declared baseline or a clean database, apply the proposed migration directory, and load the target-engine version and relevant extensions.
  2. Run schema linting. Use the linter’s local, linked, or URL-addressed database mode as appropriate. Supabase’s CLI reference names supabase db lint for linting a local database for schema errors; its options include warning and error levels and controls for whether findings fail CI.
  3. Lint the migration files. Run the migration tool against the exact files in the pull request. Atlas documents atlas migrate lint for destructive operations, breaking changes, locks, rewrites, and custom rules.
  4. Apply in isolation. Execute the migrations on a disposable database populated with production-like schema and representative data. Capture duration, lock waits, errors, and row counts rather than relying only on a successful exit code.
  5. Verify the resulting schema. Dump the database before and after the migration, compare the committed schema output with the generated result, and rerun schema linting. GitLab’s documented db:check-migrations job follows this pattern and also checks rollback results.
  6. Review rollback or forward-fix behavior. If the production strategy is an irreversible forward fix rather than a down migration, document that explicitly, test the recovery procedure, and state what data cannot be restored.

Run the same checks locally where possible so a developer can fix a finding before CI consumes a database. CI remains the authoritative gate because it uses a clean baseline and a reproducible engine version.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do common tools fit into this distinction?

What does JetBrains migration comparison provide?

JetBrains’ migration workflow compares an origin object with a target object, shows new, modified, and deleted objects, and generates SQL to make the target match the origin. It is primarily a change-generation and review aid. Treat the generated statements as migration input, then subject them to engine-specific safety linting and execute them against an isolated copy.

What does Supabase database linting provide?

The Supabase CLI command supabase db lint checks a local database for schema errors. It can work with local, linked, or URL-addressed databases and can distinguish warning and error findings, with fail-on controls suitable for CI. It does not replace a migration linter that examines whether a particular SQL change will lock, rewrite, delete, or break compatibility.

What does Atlas migration linting provide?

Atlas states that atlas migrate lint analyzes migration files for destructive operations, breaking schema changes, table locks or rewrites, and custom rules. It is intended for both local use and CI. Its output should be interpreted with the target engine, version, table size, and workload in mind; a generic warning is a prompt for an execution decision, not a universal estimate of lock duration.

What does GitLab’s migration check add?

GitLab requires database review for changes that alter schema or perform data migrations. Its db:check-migrations job compares schema dumps before and after migrations, checks rollback results, and compares committed schema output with the result produced by the migrations. This verification layer catches drift and incomplete migration coverage that either linter could miss by itself.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Which linter should answer a particular database question?

Question Primary check Evidence to inspect
Are all foreign keys compatible and properly backed by unique keys? Schema lint Assembled target schema, relationship graph, key types, collations, and uniqueness
Will dropping this column remove data or break an older application? Migration lint plus application compatibility review Migration SQL, dependency scan, release versions, and rollback or forward-fix plan
Could adding an index block writes? Migration lint Engine/version behavior, index size, online or concurrent option, lock policy, and test timing
Does every table have the required audit columns? Schema lint Complete model and organization naming or policy rules
Did the migration actually produce the committed schema? Migration verification Before-and-after dumps, migration history, and generated target schema
Has a direct production edit bypassed version control? Baseline and drift check Live dump compared with declarative files and recorded migration history

How do you troubleshoot a failed lint or verification check?

The migration linter flags a destructive statement.

First confirm whether the operation is intentional. If it is, separate removal from the compatibility window: stop old readers and writers, take any required backup or archival copy, obtain the explicit approval required by policy, and schedule the operation. If it is not intentional, regenerate the migration from the correct baseline rather than suppressing the warning.

The linter reports a lock or rewrite risk.

Check the target engine and version, table and index sizes, active traffic, and whether an online or concurrent form exists. Test the exact statement on a production-like clone, measure lock waits and elapsed time, and consider a phased backfill or a separately scheduled DDL step. Do not convert a warning to success merely by adding an ignore rule without recording the operational rationale.

Schema linting reports an incompatible relationship.

Inspect both columns and every object that depends on them. Align types, signedness, precision, collation, and nullability deliberately; verify that the referenced key is unique; then regenerate or edit the migration so the corrected model is what CI assembles. Re-run schema linting after applying the migration to a clean database.

db push reports a synchronization error.

Compare the remote schema dump with the declarative files and migration history. Identify direct edits, decide whether they belong in a new versioned migration, and repair the baseline before pushing again. Avoid marking the history as applied until the actual remote structure and the recorded state agree.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rollback testing fails.

Determine whether the migration is inherently irreversible, such as a data deletion or lossy type conversion. If restoration is possible, test it from a real backup or documented snapshot; otherwise use a forward-fix plan, preserve the data needed for recovery, and make the limitation visible in the pull request and release runbook.

What should a pull request checklist contain?

  • The source of truth used for the target schema: declarative files, migration history, or a reconciled live dump.
  • A schema-lint result for the complete target model, including relationship and cross-object checks.
  • A migration-lint result for destructive operations, compatibility, locks, rewrites, transactions, backfills, and custom policy rules.
  • Evidence from an isolated apply on the target database engine and version.
  • Before-and-after schema dumps, row-count or checksum checks where data moves, and confirmation that required objects remain present.
  • An application compatibility plan for mixed-version deployments, including when old code stops reading or writing the changed objects.
  • A rollback test or an explicit forward-fix procedure that states what cannot be undone.
  • An owner and an operating window for any change that may hold locks or consume substantial I/O.

Frequently Asked Questions

Do schema and migration linters inspect application code too?

Not by default. They analyze database definitions and migration context; detecting an ORM query or an application binary that still references a column usually requires a separate dependency scan, compile-time check, or compatibility test in the deployment pipeline.

Can I lint migrations generated by an ORM?

Yes, provided the generated SQL is captured as the migration artifact and the linter supports the target engine. Review the generated file rather than the model diff alone, because ORM output can include implicit index drops, type changes, or transaction wrappers that affect deployment safety.

How should a team handle an intentional lint exception?

Keep the exception narrow, attach the reason and approval to the migration, and set an expiry or removal condition when possible. The pull request should record the tested lock behavior, data-retention decision, and recovery plan so a future reviewer does not mistake the exception for a harmless default.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What database state should CI use for realistic lint results?

Use the same engine family and major version as production, include extensions and permissions that affect DDL, and load representative table sizes for lock and rewrite tests. A tiny empty database is useful for syntax and relationship checks but cannot reveal workload-sensitive timing or resource pressure.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

database CIdatabase schemaDevOpsmigration lintingschema lintingSQL migrations

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.