Data changes have a nasty property that code changes mostly lack: the damage persists. Redeploy an old build and the bug is gone; restore an old table and you may have lost a day of legitimate writes. That is why reversible decisions in DataOps deserve a blunt comparison rather than a slogan. Below, four common ways of changing a live dataset are set side by side, judged on one thing only: how hard is it to get back to where you started?
The yardstick: cost of getting back
Forget elegance and speed for a moment. For each approach, ask three plain questions. How long until I can undo it? What do I lose if I do? And how soon does the undo option expire? That third question is the one people forget. Many “reversible” changes are only reversible for a window, until old files are vacuumed, a retention period lapses or consumers start depending on the new shape.
Option one: change it in place
You rename the column, rewrite the table, or overwrite the partition, and everything downstream sees the new state at once.
- Undo time: usually hours, sometimes days, because it means restoring from a backup and replaying writes.
- What you lose: any writes since the backup, unless you replay carefully.
- When the window closes: the moment the first downstream job or dashboard consumes the new shape.
- Verdict: fine for scratch tables nobody else reads, risky anywhere else. It is the closest thing to a locking door in this list.
Option two: expand, migrate, then contract
You add the new column or table next to the old one, write to both, move readers over one at a time, and remove the old structure only when nobody touches it.
- Undo time: minutes during the first two phases, since readers just point back at the old structure.
- What you lose: almost nothing, apart from some storage and extra write work.
- When the window closes: at the contract step, when you drop the old structure. That step is the one irreversible moment, and it comes late, with evidence.
- Verdict: the workhorse. It converts one scary jump into several small hops, and only the last hop is a one-way one.
Option three: build the new thing alongside and compare
Instead of altering anything, you produce a second version of the output dataset through a new pipeline and diff it against the existing one for a while. Consumers keep reading the original until the diff is boring.
- Undo time: none needed, because nothing was changed. You simply never switch.
- What you lose: compute and storage for the duration, plus the effort of reading diffs.
- When the window closes: never, until you retire the old pipeline.
- Verdict: the safest option for transformation logic you do not fully trust, though it costs money and attention. Worth it for revenue, compliance or customer-facing tables; overkill for internal helper tables.
Option four: lean on versioned storage
Many modern table formats and warehouses keep history, letting you query or restore a table as it was at an earlier point. If your platform offers it, a bad write becomes a restore to a recent version rather than a recovery project.
- Undo time: minutes, if you have rehearsed it.
- What you lose: writes made after the version you restore to, unless you reapply them.
- When the window closes: when retention expires or a cleanup job removes old versions. Check the retention setting before you rely on it, because the default is often shorter than people assume.
- Verdict: an excellent safety net, but a net with an expiry date. It complements the expand-contract pattern rather than replacing it.
Side by side
Put plainly, the options rank like this for reversibility: building alongside is easiest to walk back, expand-and-contract is nearly as good, versioned storage is good while the history lasts, and in-place edits come last. The order flips if you rank by effort, which is exactly why teams drift toward the riskiest option under deadline pressure. A reversible design costs more at the start and pays back the first time something goes wrong.
Reversibility in the pipeline, not just the table
The same sorting applies to pipeline behaviour. A few practical examples:
- Idempotent loads. If rerunning a job produces the same result rather than duplicate rows, a failed run is a rerun, not an investigation. That one property turns most incidents into minor ones.
- Append, then publish. Land new data in a staging area, check it, then swap it into the published location. The swap is the commitment; everything before it is disposable.
- Quarantine, not delete. When a validation rule rejects rows, move them to a holding table. Deleted rows cannot be reviewed; quarantined rows can be released once the rule is fixed.
- Soft deletes. Mark records as removed before physically removing them, with a grace period.
Validation tooling helps here because it makes the “publish” step conditional. The hands-on piece on declaring data expectations with Great Expectations shows how to gate a load on checks, which is the pipeline equivalent of a door that only opens when conditions are met. Likewise, mastering transformations with dbt gives you versioned, reviewable models, so a logic change is a diff you can revert in version control instead of a mystery edit.
The irreversible list: be strict about it
Some data decisions really are one-way, and pretending otherwise is how teams get hurt. Treat these with evidence and a second reader:
- Physically deleting source data with no copy elsewhere.
- Sending data to an external party, whether a partner feed, a published dataset or a customer export.
- Changing identifiers that other systems have stored, such as customer keys.
- Dropping the old structure at the end of an expand-contract migration.
- Anonymising or aggregating raw records in a way that cannot be rebuilt, which can be the right move for privacy but should be taken on purpose.
The rest of your week is probably made up of choices that can be tried cheaply. Spend your caution on that short list and let the rest move.
A worked example: splitting a name field
Suppose a customer table holds a single full-name column, and the marketing team wants first and last names separately. The in-place route rewrites the column and breaks every report that reads it. The reversible route adds two new columns, fills them from the old one with a script you can rerun, and updates the loader to write all three for a few weeks. Reports move across one at a time, each with its own small announcement. When the last reader has switched and a week of quiet has passed, you schedule the drop of the old column and take that step deliberately, with a snapshot taken minutes before. If someone discovers a report that was missed, the old column is still there and nobody has to explain a gap. The total extra effort is some duplicated writes and a calendar reminder, which is a small fee for removing a whole category of surprise.
Where outside help fits
Teams sometimes ask for help working out which of their migrations are the locking kind. A consultancy and training firm in Amsterdam, DevOpsSchool Netherlands, works on DevOps, SRE and cloud delivery and runs corporate and instructor-led courses, which is the sort of place engineers go when they want release and rollback habits taught properly alongside the data tooling.
A practical checklist before any change
- Write down the exact command or step that undoes this change, before you make it.
- Check how long that undo remains possible (retention, vacuum jobs, expiring snapshots).
- Note who or what reads this data. If an outside consumer does, treat it as the locking kind.
- Run the undo once on a copy. An untested rollback is a hope.
- Decide the contract date in advance, and calendar it, so old structures do not linger forever.
Bottom line
Do not try to make every data change reversible; that way lies endless parallel pipelines and a budget nobody approved. Sort changes by who consumes them and how long the undo stays alive. Use expand-and-contract as your default, versioned storage as your net, and quarantine instead of deletion. Keep your irreversible list short, written down and reviewed by a second person. Everything else, try it, watch it, and be ready to step back.