Prasoon AI
ServicesInsights
Let’s talk
GOOD IDEAS DESERVE GREAT ENGINEERING.Explore the possibilities.

WHAT I BUILD

AI systems

Agents, private knowledge, and production AI.

SaaS platforms

Scalable software, from first release to growth.

HOW WE WORK

Services

Engineering expertise for your next challenge.

Industries

Solutions grounded in your business context.

IDEAS & PERSPECTIVES

Insights

Practical thinking on AI and architecture.

About my approach

Bridging research and production.

Available for select projectsDiscuss your project
All insights/Database Engineering

Database Engineering / Practical engineering

Live PostgreSQL Migrations: Expand, Backfill, Verify, Contract

Change a busy database through compatibility phases, bounded backfills, lock budgets, and measurable cutover gates instead of betting everything on one deployment.

P.
Prasoon ThakurAI systems architect
September 20, 20265 min read
THE ENGINEERING SERIESDatabase Engineering
COMPATIBILITY WINDOWChange without a cliff.
01Expand
02Backfill
03Contract
Old reads → verified cutover → new reads
Database EngineeringIdeas, connected to implementation.
In this article6 sectionsContents +
  1. 01A migration is a compatibility program
  2. 02Expand without changing the meaning of existing writes
  3. 03Backfill through small resumable transactions
  4. 04Build constraints without surprising the workload
  5. 05Verify semantics before switching readers
  6. 06Separate code rollback from data rollback

A migration is a compatibility program

Consider replacing a free-text account status with a constrained lifecycle model while requests, background jobs, and exports continue running. Renaming the column and deploying new code together assumes every process changes at the same instant. A rolling deployment makes that assumption false.

Treat the change as a sequence of compatible states. At each state, specify which application versions can read and write, which data representations exist, and how to stop safely. The objective is bounded operational risk. No migration procedure can promise zero downtime independently of workload, lock contention, and operator readiness.

PostgreSQL documents the lock requirements of ALTER TABLE; many forms acquire an access-exclusive lock. A short operation can still wait behind a long transaction. Establish a lock-wait budget and cancel rather than allowing an unexpected blocking queue to grow. PostgreSQL ALTER TABLE

Expand without changing the meaning of existing writes

Add the new representation first. Deploy readers that tolerate its absence. Choose one authoritative write path and update both representations atomically while the compatibility window remains open. If multiple applications write directly, a temporary database trigger may be more reliable than hoping every client implements dual writes.

Define the conversion precisely. What does an unknown legacy status become? Can two old values map to one new value? Is reverse conversion possible? These answers determine whether rollback is a code change or a data-reconstruction project.

PhaseRead behaviorWrite behaviorExit evidence
ExpandExisting fieldExisting fieldSchema usable by old release
SynchronizeExisting fieldBoth fields atomicallyAll writers upgraded or intercepted
BackfillExisting fieldBoth fields atomicallyNo unexplained missing conversions
Cut overNew field with monitored fallbackBoth fieldsDomain-level reconciliation passes
ContractNew fieldNew fieldOld dependencies and rollback window retired

Backfill through small resumable transactions

Use indexed key ranges, a durable checkpoint, and bounded batches. Do not hold one transaction across millions of updates. Record the transform version so a resumed worker cannot silently mix conversion rules from two deployments.

The following illustrates a bounded batch for a hypothetical status migration. The actual mapping should be reviewed against production values before use.

System Source
WITH batch AS (
  SELECT id
  FROM accounts
  WHERE id > $1 AND lifecycle_state IS NULL
  ORDER BY id
  LIMIT 500
  FOR UPDATE SKIP LOCKED
)
UPDATE accounts AS a
SET lifecycle_state = CASE a.status
  WHEN 'enabled' THEN 'active'
  WHEN 'disabled' THEN 'suspended'
  ELSE 'needs_review'
END
FROM batch
WHERE a.id = batch.id
RETURNING a.id;

Skipping locked rows means a forward checkpoint can leave holes. Run a separate reconciliation pass over remaining nulls; never interpret reaching the largest ID as proof of completion. Also ensure concurrent writers cannot introduce new nulls after the sweep.

Throttle using measured replication lag, database latency, WAL growth, and lock contention. Batch size is a control variable, not a magic constant. A safe batch on a quiet staging database may be disruptive during a production import.

Build constraints without surprising the workload

For eligible constraints, separate adding the constraint from validating existing rows. Confirm the exact syntax and lock behavior for the deployed PostgreSQL version. This article's referenced current documentation is PostgreSQL 18; older releases may differ.

Concurrent index creation allows writes to continue but has restrictions, takes additional work, cannot run inside a transaction block, and can leave an invalid index after failure. Inspect the index state and follow an explicit cleanup or retry procedure. Do not treat a failed migration command as an automatic rollback of every artifact. PostgreSQL CREATE INDEX

Verify semantics before switching readers

Compare distributions by account type, customer cohort, and legacy status. Count unexpected mappings and inspect their identifiers. A total row count can match even when every suspended account was converted incorrectly.

Run shadow reads that compare old and new interpretations without changing user-visible results. Sample both common and rare transitions. Include jobs scheduled before the deployment, import tools, support scripts, and analytics exports in the compatibility audit.

Set a cutover gate that can be evaluated mechanically: no unexplained mismatches, no remaining unconverted rows, bounded replication lag, and a tested fallback. Keep the old representation until that gate remains satisfied through a representative workload period.

Separate code rollback from data rollback

Before cutover, code rollback may be straightforward because old writes still work. After accepting states the old schema cannot express, reverting the application can destroy information. Define a point of no return and require a recovery plan before crossing it.

Rehearse with production-shaped volume and deliberately hold a long transaction open. Interrupt a backfill between batches. Roll the application back while dual writes remain active. The useful result is a measured recovery procedure, not merely a migration that succeeds once on an empty database.

Frequently asked questions

Is adding a nullable column always safe?

No. Even a fast metadata operation needs a lock. A long-running transaction can delay acquisition and create a queue of blocked application work.

When can the old column be removed?

After old application versions, workers, scheduled jobs, exports, and rollback procedures no longer depend on it, and the new representation has passed reconciliation.

About the author

Prasoon Thakur

Prasoon is an AI systems architect focused on reliable agents, retrieval, LLM operations, and scalable SaaS platforms. His work connects model behavior to the controls production teams need: evaluation, observability, security, and cost discipline.

GitHubUpwork profile

Need a reliable production system?

Turn the patterns in this guide into a scoped system design, delivery plan, and measurable reliability target.

Start a strategy session

Related insights

API Engineering

Cursor Pagination Under Concurrent Writes: A Consistency Contract

4 min read
Transaction Design

Inventory Reservations: Preventing Overselling Under Concurrency

4 min read

Active Now • 24/7 Availability

Engaging with teams
from Silicon Valley to Singapore.

I operate as a high-availability resource. To maintain secure collaboration, all global engagements are managed via Upwork.

Discuss your project Direct collaboration through Upwork.

Global / Remote

24/7 Timezone Agnostic

Syncing with USA, Europe, UAE & Singapore

Secure Engagement

Top Rated Expert on Upwork

Prasoon AI

Thoughtful architecture.
Software built for the real world.

Based online. Working worldwide.

Explore

AI systemsSaaS platformsServicesIndustries

Discover

Engineering insightsMy approach

Connect

Upwork GitHub Open to project inquiries

© 2026 Prasoon Thakur

Independent thinking. Dependable engineering.Back to top ↑