Symphony Apps Development logo — teal interlocking S monogram beside the studio wordmark
All writing

The migration we ran in the background for a month

Moving a client's core table to a new schema without a maintenance window meant running two versions of the truth side by side, on purpose, for weeks.

Category
Engineering
Reading time
6 min
Published
25 May 2026
Topics
Engineering, Architecture, Data

The client could not accept downtime, so a schema migration that would ordinarily be an evening's maintenance window became a month of dual writes, careful comparison, and a cutover that most users never noticed.

Why the obvious approach was ruled out

Locking the table, migrating, and releasing was the fast option and the one nobody would approve, because the product runs continuously across time zones and there is no quiet hour to use. The migration had to happen while the system stayed live and correct.

Dual writes, then dual reads

For the first two weeks, every write went to both the old and new schema. Nothing read from the new schema yet — it existed purely to accumulate data and let us compare it against the old one continuously. Discrepancies were logged, not corrected automatically, because an automatic correction can hide a bug in the migration logic itself.

What the comparison found

  • A default value that the old schema applied implicitly and the new one required explicitly, silently null in a small fraction of new rows.
  • A timezone assumption baked into one old column that the new schema had, correctly, made explicit — meaning old and new disagreed by design, and the comparison needed an exception written for it.
  • Ordering, in one case, mattered to a downstream report in a way nobody had documented, and the new schema's default ordering broke it.

None of these were migration bugs in the conventional sense. They were undocumented behaviour of the old system, discovered only by insisting on parity before switching anything over.

A migration with no downtime is not a faster version of a migration with downtime. It is a different, slower project that trades an evening of pain for a month of vigilance.

The cutover itself was uneventful

Once the discrepancy rate held at zero for a full week, we flipped reads to the new schema behind a flag, watched for a day, then removed the old writes. The most careful month of the project ended in an anticlimax, which was the entire point.

What we would do again

Running the comparison as a continuous, boring background job rather than a batch check run occasionally. The bugs we found were all low-frequency, and a batch check run once a day would have taken far longer to surface the same issues than a comparison running on every write did.

Tell us what you’re trying to ship

A first call is thirty minutes and costs nothing. Bring the problem, not a spec — working out what to build is the part we are good at.

Or email office@symphonyapps.ro. We reply within one business day, in English or Romanian.