Karya Semi
HomeBlogSearchCategoriesAboutContact
Karya Semi

Less noise. More notes.

HomeBlogAboutContactPrivacy PolicyDisclaimer

© 2026 Karya Semi. All rights reserved.

XGitHubLinkedIn
  1. Home
  2. /Categories
  3. /Technology

Choosing a Database Migration Strategy for Production Teams

Compare expand-and-contract migrations, backfills, locks, rollback limits, monitoring, and release sequencing for safer production database changes.

Dian Rijal Asyrof/July 20, 2026/5 min read
Illustration for Choosing a Database Migration Strategy for Production Teams

Database migrations are code changes with a longer memory. An application can roll back in seconds, while a column rewrite or data backfill may keep running after the deploy has been abandoned. is easy to discuss in slogans and harder to operate in production. The useful questions are smaller: what breaks, what gets measured, and what can a team recover without improvising at the worst possible time? This guide focuses on those questions.

A good implementation does not try to eliminate every failure. It makes failure visible, limits the damage, and gives the next person a clear path back to a working system.

Start with the compatibility window

The safest migration plan begins by asking which application versions may talk to the database at the same time. During a rolling deploy, old and new code overlap. The schema must tolerate that overlap.

This is why expand-and-contract works so well. Add the new structure first, teach the application to write both forms, backfill existing rows, switch reads, and remove the old form later.

Treat rollback as a separate problem

A deployment rollback does not automatically undo a migration. A new column can remain harmless, but a renamed or deleted column can make the previous application version fail.

Write the rollback boundary in the plan. Some changes are reversible, some are forward-only, and some need a restore procedure. Saying this early prevents a false promise during an incident.

Control locks and backfills

Large updates can compete with customer traffic. Run backfills in batches, commit regularly, pause when replication lag or latency crosses a threshold, and make the job restartable.

Before running a migration, inspect the database plan and expected lock behavior in a staging copy with realistic row counts. A migration that is fast on a laptop may be dangerous on a table with years of production history.

Observe the release

Monitor error rates, query latency, lock waits, replication lag, and the count of rows processed. Put the migration job behind a clear owner and make its state visible.

A dashboard is useful only if someone knows what action follows a bad signal. Define the pause threshold and the person who can stop the job before the change begins.

Sequence the work

Keep schema setup, application support, backfill, read cutover, and cleanup as separate releases when the risk is high. Smaller steps create more checkpoints.

For a small change, one release may be enough. The point is not ceremony. The point is keeping each step understandable when the database behaves differently from the test environment.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

Write the failure case down before writing the happy path. This forces the design to name its assumptions. It also gives reviewers something specific to challenge instead of asking whether the system feels safe.

Small teams should prefer boring controls that are easy to inspect. A short log, a durable record, and one tested recovery command often beat a large platform that nobody owns.

The best signal is usually close to the user-visible outcome. Track whether work was accepted, completed, retried, or abandoned. Internal counters help, but they should explain the outcome rather than replace it.

Run the procedure in a test environment and then repeat it during a quiet production window. Documentation that has never been exercised is a guess with formatting.

DR

Dian Rijal Asyrof

Writes about useful AI tools, programming practice, and the craft of building reliable software.

Previous articleHow to Design a Reliable Webhook ReceiverNext articleHow to Evaluate a RAG Application With a Regression Test Set
DatabasesMigrationsSoftware Engineering
On this page↓
  1. Start with the compatibility window
  2. Treat rollback as a separate problem
  3. Control locks and backfills
  4. Observe the release
  5. Sequence the work

On this page

  1. Start with the compatibility window
  2. Treat rollback as a separate problem
  3. Control locks and backfills
  4. Observe the release
  5. Sequence the work

See also

Illustration for GLM 5.2 and the Coming AI Margin Collapse: What Open-Weights Models Mean for API Providers
Software Engineering/Jul 15, 2026

GLM 5.2 and the Coming AI Margin Collapse: What Open-Weights Models Mean for API Providers

A Chinese open-weights model just matched GPT and Opus performance. Here's why that changes the economics of AI inference for every developer.

3 min read
AISoftware Engineering
Illustration for The Developer's Trap: Why Git Signed Commits Don't Guarantee Codebase Security
Software Engineering/Jul 15, 2026

The Developer's Trap: Why Git Signed Commits Don't Guarantee Codebase Security

Mandating Git commit signing is a trending compliance requirement. But relying on the green badge creates a false sense of security that leaves repositories vulnerable.

5 min read
GitSecurity
Illustration for Error Budgets for Small Engineering Teams: When to Slow Down and When to Ship
Software Engineering/Jul 3, 2026

Error Budgets for Small Engineering Teams: When to Slow Down and When to Ship

Error budgets sound like an SRE concept for big companies. Turns out they're useful for any team that ships software and wants to make deliberate decisions about reliability vs velocity.

4 min read
Software EngineeringReliability