Free — no signup required

The Expand-Contract Pattern: Safe Schema Changes

2 min read

The Problem with Naive Schema Changes

Before diving into tools, you need to understand the most important pattern in database CI/CD: Expand-Contract (also called Parallel Change). Without this pattern, even the best tooling will cause production outages.

The naive approach to renaming a column is a single, direct rename statement. That single statement will break any running application instance that still references the old column name email. In a rolling deployment where old and new pods coexist, this causes immediate errors.

The Three-Phase Safe Migration

The Expand-Contract pattern splits a breaking change into three separate deployments:

Phase 1 — Expand: Add the new column (email_address) alongside the old, nullable and with no constraints yet, so both old and new application code can work. Deploy application code that writes to both email and email_address.

Phase 2 — Migrate: Backfill email_address from the existing email values for every row where it's still empty, then — once all rows are populated — add the NOT NULL constraint. Deploy application code that reads from email_address and writes to both.

Phase 3 — Contract: Once all application instances use the new column, drop the old email column. Deploy application code that only references email_address.

This pattern means no single deployment is a breaking change. Each step is independently safe to roll back at the application layer.

Interview Tip

A common interview question is: "How would you rename a column in a production database with zero downtime?" The wrong answer is ALTER TABLE users RENAME COLUMN. The correct answer describes the Expand-Contract pattern across three separate deployments, explaining why each phase is necessary and what would break if you skipped directly to the final step.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free