top of page

Batch Database Migrations: Why “All-or-Nothing” Isn’t Always Enough

  • Writer: Oshri Cohen
    Oshri Cohen
  • Jun 18
  • 5 min read

Anyone who’s led a software deployment or managed a data platform knows this scenario: your team has lined up a stack of database migrations, schema tweaks, type changes, and data transforms ready to deploy with the latest version of your service. To stay safe, you wrap all those migrations into a single transaction, reasoning: “If anything fails, just roll it all back!” Sounds perfect, right?


If only.


This approach, while comforting in its atomicity, hides subtle dangers, especially when schema or type changes are involved. And it doesn’t matter if you’re running PostgreSQL, MySQL, SQL Server, Oracle, or some new-fangled managed SQL in the cloud. The root problem crops up everywhere: some database changes only “exist” after the transaction is committed. If a later migration in the same batch depends on a change from earlier in the batch, you might find your deployment blowing up at the worst possible time.


The Problem: Transactions vs. Schema Changes


Let’s generalize the situation:


  • Migrations are run as part of the deployment process.

  • All new migrations are bundled into a single transaction, ensuring that if anything fails, all changes roll back, and your application and data are never left in a partially updated state.

  • Some migrations add new types, columns, or constraints. Maybe you’re introducing a new enum, a custom data type, or a not-null constraint.

  • But those schema changes don’t “take effect” inside the transaction, not until you commit. The next migration (still inside the same transaction) tries to reference the new type or structure, and your database says: “What new type?”


This isn’t just a Postgres quirk. Many databases treat schema or DDL (Data Definition Language) changes differently from data changes, often limiting their visibility or availability until the transaction is completed.


A Real-World Example (Abstracted)


Let’s say your batch includes:


  1. Migration A: Adds a new data type or column.

  2. Migration B: Adds a new table or column that references that type.

  3. Migration C: Adds new data to the new table.


If you apply these migrations one at a time, in separate transactions, everything will be fine. But suppose you run them together in a single transaction (as many tools default to).

In that case, Migration B might fail because, from the database’s perspective, the new type “doesn’t exist yet,” it’s not visible to the session until the transaction is committed.

Suddenly, your all-or-nothing safety net is at war with your need for incremental, dependent schema changes.


How Teams Try to Solve This

Let’s look at the most common patterns, along with their drawbacks:


1. One Migration, One Transaction (Serial Execution)

Every migration runs in its own transaction. If a migration fails, you get partial progress (some applied, some not), but you avoid the type-visibility problem. Your deployment tool or operator must be prepared to manually handle partial application rolling back, fixing, or rerunning migrations as needed.


Drawback:

If your service deployment assumes all migrations are applied together, you can end up with a mismatch between code and schema if a migration halfway through fails.


  1. All Migrations, Single Transaction (Batch Execution)

This feels safest until you hit a migration dependency on a new type or structure. If any migration in the batch fails because it can’t see the previous schema change, the whole deployment is rolled back, but you’re stuck: you can’t progress until you split migrations more granularly.


Drawback:

Batching hides subtle bugs and dependency issues, and your deployment tooling may struggle to handle the complexity, especially when mixing schema and data migrations.

3. Preflight/Promotion Pipelines

The most robust approach is to split migrations so that structural or type changes are deployed ahead of dependent migrations. For example, you might apply Migration A (type change) as its own release, let it bake in production, then deploy B and C in a later step. This ensures the database “sees” each change before it’s referenced.


Drawback:

It requires more careful planning and may slow your deployment cadence. You also need coordination so that developers and release managers understand which migrations must be deployed together and which can (or must) be deployed independently.


4. Down/Rollback Migrations

If something fails, have explicit rollback steps (“down migrations”) ready to undo partially applied migrations. Tools like Flyway and Liquibase support this, but the actual rollback logic can be complex, especially if schema and data changes are interleaved.


Drawback:

Down migrations are rarely as simple as “just undo it.” You may lose data or run into cascading dependency issues. Manual cleanup is often required.


Why Is This So Tricky?


  • Transactional DDL isn’t universal. Some databases (Postgres, MySQL with InnoDB, SQLite) support transactional schema changes; others (Oracle, SQL Server, older MySQL engines) do not, or do so with caveats.

  • Migration tools default to safety. Most modern migration tools like Alembic, Flyway, Liquibase, Knex, and others offer to wrap migrations in transactions, but this isn’t always the right call.

  • Environments diverge. In lower environments (dev, staging), migrations are often applied one at a time, as developers merge their code. In production, teams batch up all pending migrations for the release, recreating the visibility issue.

  • Batching seems like an optimization, but it hides complexity. What looks like “fewer clicks” becomes “harder to debug” when things break.


What’s the Right Approach?


1. Know your database’s transactional DDL behaviour.

Understand what can and can’t be changed inside a transaction, and how those changes propagate to subsequent migrations in the same session.


2. Use migration tooling wisely.

Configure your tool to allow splitting migration schema changes in one batch, dependent logic or data changes in another. Avoid wrapping everything in one transaction by default.


3. Favour forward/backward compatibility.

Write your code and migrations so that you can safely deploy a new schema before your application code depends on it. This may mean a two-phase deployment: introduce a new column, then, in a later deployment, write to it.


4. Automate, but verify.

CI/CD pipelines can be helpful, but human oversight remains crucial for complex migrations. Manual checkpoints, especially before major schema changes, can save hours of production downtime.


How Red Corner Fractional CTO Solves This Problem

Database migration pain is a classic example of why startups and growing teams need technical leadership. It’s not just about writing SQL, it’s about understanding deployment patterns, tooling quirks, team processes, and how your architecture choices can cause downstream risk.


At Red Corner, our Fractional CTO service can:


  • Audit your current migration approach to identify risks associated with transactional DDL or schema changes.

  • Design a promotion pipeline that splits risky migrations into safe, atomic units.

  • Select and configure the right migration tooling for your environment and database stack.

  • Train your team on writing forward-compatible migrations and handling failures gracefully.

  • Provide a fixed-cost dev team + CTO package so you never have to worry about losing nights to a broken migration or a botched rollback.


Don’t let subtle database quirks derail your release train. With the right technical leadership, you can build a deployment process that’s fast, safe, and reliable—no matter which database you use.

Sources

Need help making database migration nightmares a thing of the past? Talk to Red Corner Fractional CTO today.

 
 
 

Recent Posts

See All

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
Get a Demo

Let's Chat

TELEPHONE. 514-777-3883

Founded with Love in Montreal, Canada, but operates around the world :-)

Book a call to learn more about how Red Corner's CTO as a Service can grow and support your engineering organization today.

Thanks for submitting. We'll get back to you shortly!

bottom of page