Skip to main content
AG·SORA
Technology

Zero-Downtime Database Migration for Business Applications: Expand-Migrate-Contract, Backfills, and Rollback Plans

Published 10 min readWritten by agsora.tech
AG·SORA / BLOGAG·SORATechnologyZero-Downtime Database Migrationfor Business Applications:Expand-Migrate-Contract, Backfills,and Rollback Plansagsora.tech
In this article

Every business application that stays alive will change. New features need new columns, new reports need new tables, and data growth forces old structures to be reorganized. The problem is that the database is the hardest part of an application to change while it is in use. Program code can be replaced in seconds with a new release, but a table holding millions of rows of orders, invoices, and stock cannot be "switched off for a moment" without consequences. Many businesses end up choosing a midnight maintenance window or, worse, postponing changes until technical debt piles up.

This article explains how to change the structure of a business application's database with minimal risk and, where possible, without stopping the service: gradual change patterns, how to move large data, rollback plans, and testing. It is meant for business owners and product managers who need to understand the technical decisions behind a change, and for technical teams looking for a practical framework.

Summary

  • Safe database changes are made gradually with an "expand, migrate, contract" pattern, not in a single swap
  • Always prepare a rollback plan and a backup whose restore has been tested before touching production data
  • Move large data in small chunks in the background so tables are not locked and users are not slowed down
  • Test the migration on a copy of production data, measure the time, and rehearse the procedure before the day
  • Communicate the schedule and risks to business users; not every change needs zero downtime, but every change needs a plan
Blue and gray network cables plugged into a patch panel
Changes to the data layer should be planned like changes to physical infrastructure

Why database migration is risky

A database structure change differs from a code change for two reasons: data is permanent, and the change often affects the whole application at once.

  • **Data cannot be "replaced"**: a code mistake can be fixed with a new release, but corrupted or deleted data can only be recovered from a backup
  • **Table locking**: some structure changes lock the table while they run, so the application waits or errors
  • **Version compatibility**: during a release, old and new versions of the code may run side by side and both must fit the database structure
  • **Scale**: a change that finishes in one second on test data can take an hour on production data
  • **Hidden dependencies**: reports, integrations, and old scripts may read the column you are about to change

For this reason, a database migration should be treated as a small project with preparation, testing, and a way back, not a routine task done in passing.

The basic principle: expand, migrate, contract

The most widely used pattern for no-downtime changes is often called "expand and contract". The idea: never change something in a way that immediately breaks the old code version.

Stage 1: expand

Add the new structure without removing the old. For example, to split a full name column into first and last names, add the two new columns first. The old column stays and is still used. Adding an optional column is usually quick and safe.

Stage 2: migrate

Change the application to write to both old and new structures, then copy historical data from old to new gradually. Once all data is copied and consistent, change the application to read from the new structure. Throughout this period both code versions keep working.

Stage 3: contract

When you are sure nothing uses the old structure, stop writing to it, then delete the old column or table in a later release. Do not rush: keep the old structure for a while as a safety net.

This pattern takes more steps than changing everything at once, but each step is small, reversible, and does not interrupt service. It fits with feature flags and gradual rollouts, which allow switching to new behavior at low risk.

Types of change and their risk level

Not all changes are equal. Recognizing the risk level helps decide whether a maintenance window is needed.

Low-risk changes

  • Adding a new table that old code does not use yet
  • Adding an optional column without a heavy default value
  • Adding an index with a method that does not lock the table (where the database engine supports it)

Medium-risk changes

  • Changing a column's data type, especially on large tables
  • Adding a required column with a default value to a table of millions of rows
  • Adding a constraint to existing data, because old data must be validated

High-risk changes

  • Renaming a column or table used by many parts of the application
  • Dropping a column or table
  • Splitting or merging large tables
  • Moving the database to another engine or provider

For high-risk changes, always use the gradual pattern. A rename should be done by adding the new name, copying, switching over, and then deleting the old name, not with a direct rename command.

Moving large data without disturbing users

Copying millions of rows in one big transaction can lock the table, exhaust memory, and make database replicas lag. A safer way is to fill the data in small chunks (a backfill).

A safe backfill pattern

  • **Small chunks**: process thousands of rows per round, not millions at once
  • **Pauses between rounds**: add a short pause so database load stays low for users
  • **Resumable**: record the last position so an interrupted process can continue rather than restart
  • **Idempotent**: running the same chunk twice does not corrupt data
  • **Monitoring**: watch application response times, queues, and replication lag while it runs
  • **Timing**: run outside peak hours, such as outside store transaction hours or the month-end close

This approach is similar to background processing in event-driven architecture and message queues: heavy work is split into small units that can be retried.

Handling new data that arrives during migration

While old data is being copied, users keep creating new data. Make sure the application writes to both structures (dual write), or use a change-capture mechanism (triggers or a change log) so no row is missed. When finished, run a consistency check: row counts, total values, and random samples must match between the old and new structures.

Rollback plan and backups

The most important question before a migration is: "If this fails, how do we go back?" A team without a clear answer is not ready to migrate.

A backup that can actually be restored

Having a backup is not the same as having a restorable backup. Test the restore in a separate environment and measure how long it takes. If a restore takes six hours, a one-hour maintenance window makes no sense. The full strategy is covered in business data backup strategy and disaster recovery and business continuity planning.

The "go or roll back" decision point

Set in advance the criteria that make you cancel: for example, response time above a threshold, a rising error rate, or a failed consistency check. Decide who has the authority to decide, so there is no debate in the middle of the process.

Structure rollback versus data rollback

Restoring structure is easier than restoring data. Once users have written new data into the new structure, going back to the old one can mean losing that data. That is why dual writing helps so much: the old structure stays complete, so cancellation is safe until the contract stage.

Testing before the day

An untested migration is a gamble. Prepare a test environment that resembles production.

  • **A copy of production data**: use a copy (with personal data masked if needed) so timing and behavior are close to reality
  • **Measure duration**: record the time of each step and extrapolate for data growth
  • **Test both code versions**: make sure the old and new versions run well on the mixed structure
  • **Rehearse the execution**: run the whole procedure from start to finish, including the rollback
  • **Automated tests**: put migration scripts into the continuous integration flow, as discussed in [automated testing and CI/CD](/blog/pengujian-otomatis-dan-ci-cd-untuk-aplikasi-bisnis)
  • **User acceptance testing**: involve business users to check reports and key figures after the trial, in line with [UAT before an app launches](/blog/uat-pengujian-sebelum-aplikasi-diluncurkan)

Big migrations: changing database engine or provider

Moving a database to a different engine (for example from a local server to a managed cloud service, or from one database type to another) is a category of its own. The usual pattern:

  • **Continuous replication**: run the new database as a replica that always catches up with the old one
  • **Parallel verification**: compare the results of important queries on both databases for several days
  • **A short cutover**: at the chosen moment, pause writes briefly, confirm the replica is identical, then switch the application to the new database
  • **A standby period**: keep the old database in read-only mode for a while as a way back
  • **Application adjustments**: check behavioral differences (result ordering, time handling, case sensitivity) that can trigger subtle bugs

Strategic considerations and the danger of dependence on one provider are discussed in vendor lock-in in cloud and business software. For moving a whole system, including data, see also preparing data migration to a new system and modernizing legacy systems without stopping operations.

When a maintenance window is actually better

Zero downtime is not an absolute goal. For some businesses, a planned maintenance window is cheaper and safer than a complicated gradual design.

  • Internal applications used only during working hours, with quiet weekends
  • Small changes that need only minutes on small tables
  • A very complex one-off migration, where the risk of errors in the dual pattern outweighs the benefit
  • Businesses with clear closing hours, such as a store that closes at night

What matters is that the window is announced, short, and backed by a rollback plan. Avoid sudden maintenance without notice on peak sales days. Sync with the business calendar: avoid month-end close, payday, and promotion season.

Collaboration between business and technical teams

Database migration is often treated as a purely technical matter, yet its success depends on coordination.

  • **Business process owners** name the reports and figures that must remain correct after migration
  • **The technical team** explains risks, duration, and options in understandable language
  • **Customer support** is prepared for questions if there is a brief disruption
  • **Data owners** approve consistency criteria, for example that total balances must match exactly
  • **Documentation** records what changed, why, and how to go back, supporting [technical documentation and project handover](/blog/dokumentasi-teknis-dan-serah-terima-proyek-software)

If database performance is the reason for the migration, also review slow database and business application performance, because the problem can often be solved with better indexes or queries without a big migration.

A short checklist before running a migration

  • A recent backup exists and its restore has been tested
  • The migration script has run on a copy of production data and its duration is known
  • Old and new code versions are compatible with the structure during the transition
  • A written rollback plan with criteria and an owner
  • Monitoring is ready: response time, error rate, database load
  • The schedule has been communicated to users and customer support
  • The post-migration consistency checks are defined
  • The team is on standby during and after execution

Frequently asked questions

Can every migration be done without downtime?

Not every one. Many changes can be made gradually, but some need a brief pause, for example the final cutover to a new database engine. The target is the shortest possible planned pause.

How long should the old structure be kept before deletion?

Long enough to be sure no report, integration, or script still uses it, usually a few weeks up to one financial close cycle. Monitor usage before deleting.

Do we need to change the application just for the migration?

Often yes, at least to be compatible with two structures during the transition. The team should include this work in the estimate rather than treat it as free.

What if the migrated data turns out to be inconsistent?

That is what consistency checks and the standby period are for. If a difference is found, stop the cutover, fix the source of the problem, and repeat. The quality of old data also needs checking; see data quality and data governance.

Conclusion

Changing a database in use is a matter of discipline, not bravery. With the expand-migrate-contract pattern, moving data in small chunks, tested backups, and rehearsals on a copy of production data, a big change can be broken into small safe steps. If zero downtime is not possible, make a maintenance window that is short, announced, and has a way back.

Plan database changes without disrupting operations

Agsora helps design and carry out database migrations and business application modernization in stages, with testing and a clear rollback plan. Tell us about your current system to get an initial outline.

Free Consultation

Free checklist: choosing a software house

24 points to evaluate a vendor before you sign. Can be saved as a PDF.

Get the checklist

Ready to build a system that grows with your business?

Discuss your needs with the AG·SORA team, no cost, no commitment.