Skip to content
Cloud & Infrastructure2 min read

Database schema changes while two application versions are live

The migration is not finished when ALTER TABLE succeeds. It is finished when old and new application versions can coexist, backfill is verified and rollback still works.

PostgreSQLMySQLDeployments
Old and new application versions sharing a compatible database

A schema migration is safe during a rolling release only when every application version that may still receive traffic can read the transitional schema. Design for overlap, not for the instant when a migration command completes.

Expand first

Add a new nullable field or table without removing the old one. Deploy code that can tolerate the old and new shape. If new writes must populate both representations, define the writer and monitor drift. Do not add a non-null constraint to an unpopulated large table as a surprise in the release window.

ALTER TABLE orders ADD COLUMN customer_ref_new text;
-- Deploy dual-compatible readers and writers; backfill in bounded batches.
-- Only later enforce constraints and retire the old column.

Backfill as an operation, not a migration side effect

Process rows in bounded batches with a cursor or stable key, make the operation resumable, and record progress. Compare counts and sample records; do not assume that a completed job implies semantic equality. Plan what happens to rows written while the backfill runs.

Database engines differ in lock behavior, online index support and DDL transaction semantics. Inspect the exact operation against the deployed engine version and rehearse it on representative data. A small staging table does not expose the lock and replication effects of a large production table.

Contract after the fleet moves

Only remove the old field after all application instances, workers and external consumers have stopped reading it. Keep a clear point at which rollback shifts from a route/deploy reversal to a data restoration exercise. That point deserves explicit approval in the release plan.

For a tiny table and a maintenance window the business accepts, a simple migration may be less risky than an elaborate compatibility sequence. Use the expand-and-contract pattern where concurrent versions or table size make it necessary.

Example: renaming a customer reference

A direct rename breaks old code still deployed on one server. Instead, add a new column, teach the writer to populate it and backfill historical rows. Teach readers to prefer the new field with a temporary fallback. Once every server and worker uses the new field, enforce its constraint, stop the fallback and remove the old column in a later release.

Watch concurrent writes during the backfill. A batch copying yesterday's value must not overwrite a fresh update. Use a condition tied to source version or update time, and reconcile remaining nulls or mismatches before enforcing the new invariant.

Lock and replication budget

For every DDL statement, estimate table size, expected lock, index build behavior and replica lag on the actual database version. Set a bounded lock timeout so the migration fails instead of silently queuing application writes behind it. Inspect active transactions that might hold locks longer than expected.

Do not bundle destructive cleanup with the first release of new code. A separated contract phase buys time to observe forgotten consumers and makes application rollback possible. Document when the window closes, including backup and recovery expectations.

A rehearsal should include a worker from the old release, not just two web servers. Background jobs often outlive the web rollout and are the last writer of an old schema.

Services This Relates To

Written by KYCONNECTS Engineering.

Talk Through Your Requirements

We typically respond within 4–8 business hours.