Skip to main content
GullySystem
A practical guide

How to write a rollback plan that covers the database

Ganesh HSStrategy and Technology, GullySystem
Practical advice · 4 min read

In this guide 6 sections
The short answer

A rollback plan names the trigger, the owner and one of three routes: code rollback, roll forward with a fix, or database restore from a tested backup. Data changes are the hard part. Write it all down before release day, while nobody is under pressure and the migration has not touched a single live row.

Choose between rollback, roll forward and restore

Each failure has its own route. Rolling back redeploys the previous build, rolling forward ships a small fix on top, and restoring replaces the database with a backup taken before the migration ran.

Code-only failures favour a rollback, so a broken checkout button goes back to the last good build within minutes and nobody touches the database at all. Data damage needs a restore, and every restore loses whatever customers wrote between the snapshot and the moment someone noticed the damage.

Pick the default route before release day. Write it on the plan. Do not improvise.

  • Rollback: bad code, schema unchanged
  • Roll forward: small bug, schema already changed
  • Restore: corrupted or lost data
  • Stop: pause traffic with a maintenance page

Make migrations safe to reverse

The whole trick is that old code keeps working against the new schema, so use the expand and contract pattern and add the new column first. Ship code that writes to both columns, backfill the history in batches so the table is never locked for long, then move the reads across.

Dropping the old column waits for a later release. Never drop and deploy together. One destructive migration turns every rollback into a restore, so keep the drop in a separate later release, once the new code has proven itself.

Write a down script for each migration and run it once on a copy of production, since an untested down script is a hope and not a plan.

Take a restore point and prove it works

Take a snapshot or a pg_dump just before the migration starts, then note the time, the file size and the storage bucket on the plan.

An untested backup is only a file. Restore it into a scratch database, run counts on orders, users and invoices, and compare them with live, because any gap means the backup is unfit.

Record how long the restore took. That is your real recovery time, and it is rarely the figure people guess, so write the measured number beside the promised one.

  • Snapshot time and location
  • Restore tested on a scratch database
  • Row counts compared with live
  • Measured restore duration

Say who can call it, and when

Name one person who can call a rollback, plus a deputy, because a committee wastes the only window you have while payments flow and error responses climb.

Write triggers as plain events. Checkout fails twice in a row, or the error rate stays above baseline for ten minutes, or the payment webhook queue grows past a set length.

Agree the cut-off too. After a set time, or once new customer orders have landed, rollback is no longer clean and the team moves to roll forward. Say so in advance.

Rehearse it and keep the plan with the release

Run the rollback once on staging with the same commands and the same person, because surprises are far cheaper there than in front of customers. Time it.

Store the plan on the release ticket, not in someone’s head or an old wiki page, and keep it to one page. Short plans get read.

After each release, note whether the plan was needed and which step you would tighten, because small edits after every deployment keep the document honest.

Per-release rollback template

A one-page form. Blocks cover release name, default route, trigger events and the decision owner with a deputy, followed by a migration table that marks each row reversible or not. Lower blocks hold the restore point details, the measured restore time, the cut-off point and a rehearsal sign-off line, each with a space for initials.

Open a blank worksheet to print

Questions owners ask

Can every database migration be rolled back?

No. A down script cannot bring back dropped columns or deleted rows, so make additive changes first and delete in a later release, once the old code is gone.

When is rolling forward better than rolling back?

Roll forward when the schema has already changed and new data has been written, or when the fix is small and well understood. Going back would lose the new rows.

How often should we test the restore?

Test it before any risky release and again on a regular schedule between releases, since a backup that restored cleanly last quarter may fail after a version change.

Who should own the rollback decision?

Name one person, usually the release manager or tech lead, with a deputy beside them, and let everyone else advise from the side while that person decides. Clear authority saves minutes.

Browse all guidesBack to top ↑
Next step

Have a specific situation to work through?

This article covers the general case. Tell us what you’re actually dealing with and we’ll respond directly.

Share Your Requirements