Rollback Strategy
Design the undo plan before deploying — not during a production incident. A deployment without a rollback plan is a bet that nothing will go wrong.
⛔ The Iron Law
If you can't undo it, don't ship it yet.
Every change is classified by rollback complexity before deploy, and every non-Simple change has a written rollback procedure that has been tested — not just imagined. Decide rollback before the incident, never during it: the minutes you'd spend designing an undo mid-incident are the minutes users are down.
Step 1: Analyze the Deployment
List every change being deployed:
- Code changes: Which services, which endpoints, what behavior changes?
- Schema changes: New tables, columns, indexes, constraints, dropped columns?
- Data migrations: Backfills, transformations, deletes?
- Configuration changes: New env vars, feature flag states, infrastructure config?
- Dependency updates: Library upgrades, external service version changes?
For each change, ask: If this change caused a production incident 30 minutes after deploy, what would rollback look like?
Step 2: Classify by Rollback Complexity
Assign each change a rollback complexity:
| Complexity |
Definition |
Examples |
| Simple |
Revert the code deploy, no other steps |
Logic change, UI change, new feature behind a flag |
| Coordinated |
Code revert + one other step |
New env var (remove it), new index (drop it), additive column (leave it, no harm) |
| Complex |
Requires data migration reversal or multi-step coordination |
Column rename (requires data migration), data backfill (must un-backfill) |
| Irreversible |
Cannot be fully undone |
Destructive data operations, sent emails/notifications, external API calls with side effects |
If any change is Irreversible: explicitly document what partial rollback looks like and get sign-off before deploying.
See references/rollback-patterns.md for detailed patterns per component type.
Step 3: Design the Rollback Procedure
For each non-Simple change, write the explicit rollback steps:
Database schema rollback:
- Additive columns (new nullable column): safe to leave after rollback — old code ignores them
- Non-additive changes (rename, type change, NOT NULL constraint): require a reverse migration
- Dropped columns: need to be re-added and re-populated (use
git bisect to find the data state)
Feature flag rollback:
- Define the "off" state before deploying
- Test the "off" state in staging before deploying to production
- Know which percentage to roll back to if doing a canary rollout
Data migration rollback:
- Write the reverse migration before deploying the forward migration
- Test both on a copy of production data
- If the migration is too large to reverse quickly, plan for an emergency read-only mode instead
Configuration rollback:
- Document the previous value of every env var being changed
- For secrets rotation: keep the old secret valid for 24 hours after switching
Step 4: Document and Test the Plan
Write the rollback plan in the deployment PR or deployment runbook:
Rollback Plan:
1. [Step]: [Command or action]
2. [Step]: [Command or action]
3. Verify: [How to confirm rollback succeeded]
Estimated rollback time: [X minutes]
Rollback owner: [Who executes this during an incident]
Test it before you need it:
- Run the rollback steps in staging before production deploy
- Confirm the rollback takes the expected time (important during incidents)
- Verify application health after rollback completes
Use templates/rollback-plan.md for the full plan format.
Principles Applied
- KISS: Prefer feature flags over complex rollback procedures. The simplest rollback is turning a flag off.
- YAGNI: Don't deploy a change you can't roll back without a documented, tested plan. If you can't undo it, don't ship it yet.
- Defense in depth: Multiple rollback options (flag → revert → data migration reversal) provide fallbacks when the first option isn't enough.
- Fail fast: Define rollback triggers before deploying (see
deployment-checklist). Waiting until an incident to decide when to rollback wastes critical time.
Rationalizations to reject
| Excuse |
Reality |
| "We'll figure out rollback if it breaks" |
Designing rollback mid-incident wastes the minutes that matter most. |
| "The migration is reversible, trust me" |
Reversible in theory ≠ tested. Run the reverse on production-sized data first. |
| "It's behind a flag, that's enough" |
Only if the off-state was defined and tested before deploy. |
| "Rollback is unlikely, skip the plan" |
A rollback plan is insurance — you write it before you need it, not after. |
| "Additive column, no rollback needed" |
Confirm old code ignores it and document the no-op explicitly. |
Red flags — stop and correct course
- An Irreversible change with no documented partial-rollback and no sign-off.
- A reverse migration that has never actually been executed.
- A feature flag whose off-state was never tested in staging.
- The estimated rollback time is unknown.
Cross-Skill References
deployment-checklist — run before every deploy; rollback plan is a required gate
incident-response — execute the rollback plan when an incident occurs
configuration-strategy — design feature flags as part of config strategy (reduces rollback complexity)
1---2name: rollback-strategy3description: Design safe rollback plans before deploying — identify irreversible changes, classify rollback complexity, create tested undo procedures. Triggers: rollback plan, rollback strategy, how do I undo this, can we revert, what if the deploy fails, safe to deploy, feature flag rollout, blue-green, database migration rollback, irreversible change.4---56# Rollback Strategy78Design the undo plan before deploying — not during a production incident. A deployment without a rollback plan is a bet that nothing will go wrong.910## ⛔ The Iron Law1112**If you can't undo it, don't ship it yet.**1314Every change is classified by rollback complexity before deploy, and every non-Simple change has a written rollback procedure that has been *tested* — not just imagined. Decide rollback before the incident, never during it: the minutes you'd spend designing an undo mid-incident are the minutes users are down.1516## Step 1: Analyze the Deployment1718List every change being deployed:1920- **Code changes**: Which services, which endpoints, what behavior changes?21- **Schema changes**: New tables, columns, indexes, constraints, dropped columns?22- **Data migrations**: Backfills, transformations, deletes?23- **Configuration changes**: New env vars, feature flag states, infrastructure config?24- **Dependency updates**: Library upgrades, external service version changes?2526For each change, ask: **If this change caused a production incident 30 minutes after deploy, what would rollback look like?**2728## Step 2: Classify by Rollback Complexity2930Assign each change a rollback complexity:3132| Complexity | Definition | Examples |33|-----------|-----------|---------|34| **Simple** | Revert the code deploy, no other steps | Logic change, UI change, new feature behind a flag |35| **Coordinated** | Code revert + one other step | New env var (remove it), new index (drop it), additive column (leave it, no harm) |36| **Complex** | Requires data migration reversal or multi-step coordination | Column rename (requires data migration), data backfill (must un-backfill) |37| **Irreversible** | Cannot be fully undone | Destructive data operations, sent emails/notifications, external API calls with side effects |3839**If any change is Irreversible**: explicitly document what partial rollback looks like and get sign-off before deploying.4041See [references/rollback-patterns.md](references/rollback-patterns.md) for detailed patterns per component type.4243## Step 3: Design the Rollback Procedure4445For each non-Simple change, write the explicit rollback steps:4647**Database schema rollback:**48- Additive columns (new nullable column): safe to leave after rollback — old code ignores them49- Non-additive changes (rename, type change, NOT NULL constraint): require a reverse migration50- Dropped columns: need to be re-added and re-populated (use `git bisect` to find the data state)5152**Feature flag rollback:**53- Define the "off" state before deploying54- Test the "off" state in staging before deploying to production55- Know which percentage to roll back to if doing a canary rollout5657**Data migration rollback:**58- Write the reverse migration before deploying the forward migration59- Test both on a copy of production data60- If the migration is too large to reverse quickly, plan for an emergency read-only mode instead6162**Configuration rollback:**63- Document the previous value of every env var being changed64- For secrets rotation: keep the old secret valid for 24 hours after switching6566## Step 4: Document and Test the Plan6768Write the rollback plan in the deployment PR or deployment runbook:6970```71Rollback Plan:721. [Step]: [Command or action]732. [Step]: [Command or action]743. Verify: [How to confirm rollback succeeded]75Estimated rollback time: [X minutes]76Rollback owner: [Who executes this during an incident]77```7879**Test it before you need it:**80- Run the rollback steps in staging before production deploy81- Confirm the rollback takes the expected time (important during incidents)82- Verify application health after rollback completes8384Use [templates/rollback-plan.md](templates/rollback-plan.md) for the full plan format.8586## Principles Applied8788- **KISS**: Prefer feature flags over complex rollback procedures. The simplest rollback is turning a flag off.89- **YAGNI**: Don't deploy a change you can't roll back without a documented, tested plan. If you can't undo it, don't ship it yet.90- **Defense in depth**: Multiple rollback options (flag → revert → data migration reversal) provide fallbacks when the first option isn't enough.91- **Fail fast**: Define rollback triggers before deploying (see `deployment-checklist`). Waiting until an incident to decide when to rollback wastes critical time.9293## Rationalizations to reject9495| Excuse | Reality |96|--------|---------|97| "We'll figure out rollback if it breaks" | Designing rollback mid-incident wastes the minutes that matter most. |98| "The migration is reversible, trust me" | Reversible in theory ≠ tested. Run the reverse on production-sized data first. |99| "It's behind a flag, that's enough" | Only if the off-state was defined and tested before deploy. |100| "Rollback is unlikely, skip the plan" | A rollback plan is insurance — you write it before you need it, not after. |101| "Additive column, no rollback needed" | Confirm old code ignores it and document the no-op explicitly. |102103## Red flags — stop and correct course104105- An Irreversible change with no documented partial-rollback and no sign-off.106- A reverse migration that has never actually been executed.107- A feature flag whose off-state was never tested in staging.108- The estimated rollback time is unknown.109110## Cross-Skill References111112- `deployment-checklist` — run before every deploy; rollback plan is a required gate113- `incident-response` — execute the rollback plan when an incident occurs114- `configuration-strategy` — design feature flags as part of config strategy (reduces rollback complexity)