Zero-Downtime Database Changes: Expand-Contract Pattern for Microservices

How I perform zero-downtime schema changes while old and new application versions coexist, using additive migrations, dual-read/write transitions, backfill, verification and delayed cleanup.

Romharshan Singh
Romharshan SinghSenior Solution Architect • AI & Cloud Mentor
1 September 20263 min read0 viewsUpdated 1 Sept 2026
Zero-Downtime Database Changes: Expand-Contract Pattern for Microservices

Zero-Downtime Database Changes: Expand-Contract Pattern for Microservices

Why DB changes break safe deployment

Application rollback is easy only when the old version can still understand the database.

Bad migration:

sql
ALTER TABLE customer
  DROP COLUMN full_name;

ALTER TABLE customer
  ADD COLUMN first_name VARCHAR(100) NOT NULL,
  ADD COLUMN last_name  VARCHAR(100) NOT NULL;

If old pods still expect full_name, rolling/canary deployment breaks.

I use expand-contract.

Phase 1: Expand

Add new structures without removing old ones.

sql
ALTER TABLE customer
  ADD COLUMN first_name VARCHAR(100) NULL,
  ADD COLUMN last_name  VARCHAR(100) NULL;

Old code continues working.

Phase 2: Compatible application

New code can read new fields when present and fall back to old.

typescript
function displayName(
  row: CustomerRow,
) {
  if (
    row.firstName ||
    row.lastName
  ) {
    return [
      row.firstName,
      row.lastName,
    ].filter(Boolean).join(' ');
  }

  return row.fullName;
}

Writes may temporarily update both representations:

sql
UPDATE customer
SET
  full_name = ?,
  first_name = ?,
  last_name = ?
WHERE id = ?;

I keep dual-write windows short because they increase complexity.

Phase 3: Backfill

sql
UPDATE customer
SET
  first_name = SUBSTRING_INDEX(
    full_name, ' ', 1
  ),
  last_name = NULLIF(
    TRIM(
      SUBSTRING(
        full_name,
        LENGTH(
          SUBSTRING_INDEX(
            full_name, ' ', 1
          )
        ) + 1
      )
    ),
    ''
  )
WHERE first_name IS NULL;

For millions of rows I do this in small batches to avoid long locks/replication lag.

Phase 4: Switch reads

After verifying:

plaintext
new column populated
new app deployed everywhere
metrics clean

the application reads only new fields.

Phase 5: Contract

In a later release:

sql
ALTER TABLE customer
  DROP COLUMN full_name;

I do not combine this cleanup with the first deployment.

Index creation

Large index creation can lock or load production.

I use database-specific online/concurrent mechanisms where available and monitor:

plaintext
lock waits
replication lag
IO
CPU
query latency

Rename column

Rather than immediate rename:

plaintext
add new column
write both
backfill
read new
remove old

This supports mixed app versions.

Enum/status evolution

Never deploy code that writes a new enum value old code cannot parse if rollback is still expected.

I use additive tolerant parsing first.

Production checklist

  • Add before remove.
  • Keep new columns nullable initially when needed.
  • Deploy tolerant code.
  • Backfill in batches.
  • Monitor locks/replication.
  • Verify old/new values.
  • Switch reads.
  • Stop dual writes.
  • Wait through rollback window.
  • Remove old schema later.

FAQ

Can blue-green avoid schema compatibility?

No. Blue and green frequently share the database.

How long should expand-contract take?

Often multiple releases. Safety is more important than compressing every step into one pipeline run.

Should DB migration auto-rollback?

Destructive rollback is risky. I prefer forward-compatible migrations and roll-forward fixes.

- [Production Deployment Patterns I Have Used](/articles/production-deployment-patterns-blue-green-canary-rolling) - [Rolling Deployment](/articles/rolling-deployment-kubernetes-production)
  • Canary Deployment

  • CI/CD Promotion Pipeline

    Architect's final take

    Most zero-downtime deployment problems become database-contract problems. I treat schema compatibility as part of the release architecture, not a DBA step after coding.

Was this article useful?

Your feedback helps prioritize deeper technical content.

Romharshan Singh
ABOUT THE AUTHOR

Romharshan Singh

Senior Solution Architect and Full Stack Technology Leader with 20+ years of enterprise engineering experience across AI, cloud, distributed systems, Java, Node.js, React, Angular, Kafka and Kubernetes.