Build Logs21 October 20268 min read

Change the Schema While Old Code Still Runs

During a deploy, old and new code run together. Change the database in steps they can both tolerate, then remove the old shape after the old code is gone.

BarathProduct Engineer
FIGURE 1.0
EDITORIAL REFERENCE
Code on a monitor being inspected
CONTEXT:Code on a monitor being inspected
REFERENCE:Markus Spiske
ON THIS PAGE (7 SECTIONS)
EXECUTIVE BRIEFING · KEY TAKEAWAYS
  • ✓Martin Fowler describes parallel change as expand, migrate, then contract. The old and new shapes coexist on purpose.
  • ✓A migration that old code cannot read will fail while old instances are still in the load balancer.
  • ✓Backfill is a job with a status, not a surprise lock on the table during the request.
  • ✓Drop the old column only after no running version reads or writes it.

The migration renames a column and the new release expects the new name. For a few minutes the previous release is still running. It writes the old name. The deploy is "done" and half the writes are failing. The database was asked to be two versions at once, and the migration allowed only one.

Fowler’s parallel change, also called expand and contract, is the sequence that survives that overlap. Expand: add the new column, table, or field while the old one remains. Migrate: teach the new code to write both, backfill old rows, and read the new one. Contract: when no deployed version uses the old shape, remove it. Each step is a deploy that the currently running code can survive.

A safe order for a column change

  1. Add the new column as nullable, or with a default the old code ignores.
  2. Deploy code that writes both columns and reads the new one, falling back to the old one if the new one is empty.
  3. Backfill in batches. Watch locks and replication. A single update of every row is how a migration becomes an outage.
  4. Deploy code that reads and writes only the new column.
  5. Confirm the old column is unused in logs or in a query of live statements, then drop it in a later change.

The same sequence applies to an API field. Add the new field, keep the old one, move clients, then remove the old one in a version you actually tracked. Deleting a field in the same release that introduces its replacement assumes every client updates in the same minute. They do not.

Illustrative scenario

This scenario is illustrative. A status stored as a string becomes a foreign key to a status table. The expand deploy adds the key and keeps the string. A job fills the key from the string in batches of a few hundred rows. The next deploy reads the key. Only a later deploy drops the string. At no point does a running instance select a column that is already gone. If a batch fails, the job retries that batch. The request path never backfills the whole table.

Checklist

  • Name the versions that will overlap during the rollout.
  • Make the expand step readable by the old version.
  • Backfill in batches, with a stop if replication lag or lock time grows.
  • Contract in a separate change, after the old version is gone.
  • Rehearse the rollback of the code. Rolling code back onto a dropped column is not a rollback.
  • Treat a model that writes rows the same way. The schema it fills still has to tolerate two releases.

Questions that usually come next

Can we take a maintenance window instead?

Yes, when you truly stop all writers. Say so, and make the stop real. A window that still has a replica or a worker writing is an overlap, and the expand step is still required.

What about a tool that generates the migration from the model?

The tool will often emit the rename or the drop as one step, because the model only has the new shape. Split that output. The tool does not know which versions are still running.

Direct answers

Can we take a maintenance window instead?

Yes, if every writer actually stops. If any replica or worker still writes, you still have an overlap and you still need the expand step.

Will an ORM migration handle this?

It will generate the difference between models. You still have to split expand, backfill, and contract so the previous release keeps running.

ACADEMIC & INDUSTRY CITATIONS

References & Prior Art

REF 01Engineering essay

Parallel Change

Martin Fowler • martinfowler.com (2014)

Source
VERIFIED CONTRIBUTOR

Barath

Product Engineer

Barath builds production internal tools, double-entry financial ledgers, audit trail integrity, and supervised engineering workflows at Vasiva.

CURATED ARCHIVE

Continue Reading

Contextually relevant engineering logs and production architecture notes.

SYSTEM ADVISORY

Have an AI workflow or complex system worth exploring?

Work directly with the senior engineers who designed and shipped these systems. Zero vendor lock-in, deterministic architectures, and first-party audit integrity.

Evaluate the opportunity