neart.ai
EcosystemStoryHow We BuildPricingBlog
Try Inspected →
neart.ai
EcosystemStoryHow We BuildBlog

Ní neart go cur le chéile

A SaltCore Group Limited company

© 2026 neart.ai · SaltCore Group Limited. All rights reserved.

Software Quality

How Do You Run Database Migrations Without Breaking the Running App?

11 November 20254 min read

## The short answer


You avoid breaking a running application during a database migration by making every schema change backward compatible and rolling it out in stages, so that old and new versions of your code can both run against the database at the same time. The pattern that achieves this is expand and contract: first add the new structure without removing the old, then migrate code and data, and only later remove the obsolete pieces once nothing depends on them. Destructive, single-step migrations are one of the most common and most damaging sources of release regressions, because they break the application the instant they run.


## Why naive migrations cause regressions


During any deploy there is a window where old and new code run simultaneously: old instances are still serving traffic while new ones start up. If your migration renames or drops a column the old code still uses, those old instances start throwing errors immediately. The same happens on rollback if the new schema is incompatible with the previous code. The migration itself was "successful" but it caused a regression.


The rule that prevents this: at every moment, the database schema must be compatible with both the currently deployed code and the code you are deploying.


## The expand and contract pattern


Break any breaking change into a sequence of individually safe steps.


- Expand: add the new column, table, or index alongside the existing structure. The old code ignores it; the new code can start using it.

- Migrate: backfill data into the new structure and switch code to read and write it, often behind a flag.

- Contract: once no code reads the old structure, remove it in a later release.


A column rename, which sounds trivial, becomes: add the new column, write to both, backfill, switch reads to the new column, stop writing the old, then drop the old column. Tedious, but each step is safe to deploy and to roll back.


## Make backfills safe


Backfilling existing rows is where migrations often go wrong at scale.


- Backfill in batches rather than one enormous transaction that locks the table.

- Run the backfill as a background process separate from the schema change.

- Make it resumable so a failure halfway through does not leave you stuck.

- Verify counts and a sample of values before switching reads to the new column.


A long-running migration that holds a lock can take the whole application down as effectively as any bug.


## Mind the locks and the timing


Even adding structure can be risky on a busy database if it takes a heavy lock.


- Know which operations lock tables in your database engine and for how long.

- Add indexes concurrently where your engine supports it.

- Set sensible timeouts so a stuck migration fails fast rather than wedging the system.

- Prefer many small migrations to one large one.


## Always have a way back


The reason backward compatibility matters so much is rollback. If you need to revert the application to the previous version, the schema must still support it.


- Never drop or rename in the same release that stops using the old structure.

- Keep at least one release of overlap between expand and contract.

- Test rollback explicitly: deploy the migration, then run the previous app version against the migrated schema.


At neart.ai we build enterprise-grade products where schema changes are expected to be forward and backward compatible across deploys, so a release can always be reversed without a data emergency.


## A safe migration checklist


1. Is the change additive, or have you split it into expand and contract steps?

2. Can both the current and the new code run against the new schema?

3. Is the backfill batched, resumable, and verified?

4. Will any operation take a lock that could stall live traffic?

5. Have you tested rolling the application back against the migrated schema?


## Takeaway


Migrations break running apps when they are destructive and instant. Make every schema change backward compatible, use expand-then-contract so old and new code coexist, backfill safely in batches, and keep a release of overlap so you can always roll back. Treat the schema as something that must serve two versions of your code at once, and migrations stop being a release hazard.

Related posts

Software Quality

How Do You Ship Software Without Regressions?

Software Quality

What Is End-to-End Testing, and When Should You Use It?

Software Quality

What Does WCAG 2.2 Mean for Your Testing Strategy?