Safe Deployment and Rollback Strategies Questions
Releasing changes to production safely and incrementally, and recovering when they fail: blue-green, canary, and rolling deployments, feature flags, dark launches, traffic shifting, and progressive rollout, together with rollback strategies, safe-deploy practices, blast-radius containment, automated recovery, and safe forward/backward migration. Covers deployment orchestration across cloud platforms, staged exposure of new behavior to users, assessing deployment risk, designing reversible releases, and restoring a known-good state quickly. Focuses on how a release reaches production and how it is unwound on failure, distinct from broader incident command, which lives in Enterprise Operations & Incident Management.
You're an SRE at an org whose release process has no formal rollback policy, and engineers are reluctant to pause a release. How would you get product and engineering to adopt SLOs, rollback runbooks, and automated gates while preserving reasonable velocity?
Sample Answer
Direct answer
Getting a team to adopt SLOs, rollback runbooks, and automated gates when there's currently no formal policy and engineers are reluctant to pause releases is fundamentally a trust and incentive problem before it's a technical one: you need to show, cheaply and with data, that the current lack of process is already costing them something they care about, then introduce the change in a way that doesn't feel like it's slowing them down for its own sake.
Structured elaboration
- Start with data, not policy: pull the team's actual recent incident history and show the pattern (how many were release-caused, how long they took to resolve, what would have caught them earlier). A concrete "here's what this cost us last quarter" lands better than an abstract argument for process.
- Propose the smallest viable version first: a lightweight SLO on the single highest-value service, a one-page rollback runbook for the most common failure mode, not a company-wide mandate on day one. Small, visible wins build the credibility to expand.
- Frame gates as protecting velocity, not blocking it: an automated error-budget gate that only fires when things are ALREADY going wrong lets a team ship faster and with more confidence the REST of the time, since they're not manually second-guessing every release out of institutional caution.
- Involve the reluctant engineers in DEFINING the SLO and the runbook, rather than handing them a policy from outside; people who help write a threshold are much less likely to see it as an arbitrary obstacle later.
- Make the automation the "bad cop" instead of a person: once a gate exists, "the pipeline blocked it" is a much less politically fraught conversation than a manager or SRE manually saying no to a release.
Worked example
A team resisting SLOs after a string of near-misses: rather than proposing a full SLO framework, start with one metric (error rate) on their single most customer-visible endpoint, set a deliberately generous initial threshold (so it almost never fires early on and doesn't feel punitive), and pair it with a one-page runbook for their most common incident type written WITH the on-call engineers, not handed to them. After a quarter of it quietly preventing one or two bad releases from reaching full rollout, the team is far more receptive to extending the same pattern to other services, because they've seen it work rather than been told it will.
Trade-offs and pitfalls
Moving too fast with a heavy, comprehensive rollout of SLOs/gates/runbooks all at once tends to trigger exactly the resistance you're trying to overcome, since it reads as bureaucracy imposed from outside; moving too slowly risks another preventable incident happening before the safety net exists. The balance is a small, credible first step that earns the trust to expand, rather than either extreme.
Write a script that polls a service's health endpoint for a few minutes after a deploy, aggregates success rate and average latency, and marks the deployment FAILED if success rate or latency crosses a threshold.
Sample Answer
Direct answer
A post-deploy health-poll script watches the new version for a fixed window right after it starts receiving traffic, aggregating success rate and latency, and explicitly marks the deployment failed (triggering whatever the pipeline's next step is, typically an automatic rollback) if either crosses a threshold, rather than assuming silence means success.
Structured elaboration and worked example (logic verified)
import time
import requests
def poll_health(url: str, duration_seconds: int = 300, interval_seconds: int = 5,
success_rate_threshold: float = 0.99, latency_threshold_ms: float = 500):
# Polls a health endpoint for `duration_seconds`, aggregating results.
# Returns (passed: bool, summary: dict).
start = time.monotonic()
successes = 0
total = 0
latencies = []
while time.monotonic() - start < duration_seconds:
req_start = time.monotonic()
try:
resp = requests.get(url, timeout=5)
elapsed_ms = (time.monotonic() - req_start) * 1000
latencies.append(elapsed_ms)
total += 1
if resp.status_code == 200:
successes += 1
except requests.RequestException:
total += 1
latencies.append(latency_threshold_ms * 10) # count a hard failure as very slow, not silently dropped
time.sleep(interval_seconds)
if total == 0:
return False, {"reason": "no requests completed, cannot assess health"}
success_rate = successes / total
avg_latency = sum(latencies) / len(latencies)
passed = success_rate >= success_rate_threshold and avg_latency <= latency_threshold_ms
return passed, {"success_rate": success_rate, "avg_latency_ms": avg_latency, "requests": total}
Key structural decisions: a network exception is counted as a failed, slow request rather than silently skipped, since a connection timeout is itself a strong negative signal that shouldn't be excluded from the aggregate just because it didn't return an HTTP status code at all. Zero completed requests is treated as an explicit failure with a clear reason, never silently passed, since "we don't have enough data" should never be mistaken for "it's healthy."
Trade-offs and pitfalls
A fixed polling interval (5 seconds here) trades responsiveness for load on the health endpoint; a service under real stress from a bad deploy doesn't need to be hammered by an aggressive health-check loop on top of everything else. The retry/timeout handling matters more than it looks: a script that raises an unhandled exception on the FIRST network hiccup, rather than counting it as a data point and continuing, produces a false "script crashed" result instead of the actually useful "service is unhealthy" signal the pipeline needs to act on.
Design an automated rollback approach for a stateful service whose release includes a database migration, using blue-green environments plus a read-only clone of the database for pre-migration verification. How do you minimize data loss and handle replication lag?
Sample Answer
Direct answer
Combining blue-green with a read-only database clone lets you validate a migration's effect on real, current data BEFORE committing to it on the live database: the clone gets the migration applied first, in isolation, so you catch a problem against production-representative data without any risk to the actual live system, and the blue-green switch itself still gives you fast rollback for the application layer once you do commit.
Structured elaboration
- Clone the production database (read-only) into an isolated environment and apply the migration to the CLONE first, validating both that the migration runs successfully and that the resulting data is correct, against real data characteristics (volume, distribution, edge cases) that synthetic test data might miss.
- If clone validation passes, apply the actual migration to production using the same discipline covered elsewhere (backward-compatible, expand-contract, batched for large tables), since the clone validated the LOGIC and DATA EFFECT, not the operational safety of running it against a live, concurrently-written system.
- Blue-green for the application layer: once the schema is safely migrated (backward-compatible, so both old and new app code can run against it), deploy the new application version to the green environment, validate it, and cut over traffic, keeping blue as an instant application-level rollback path.
- Minimizing data loss and handling replication lag: the production migration's write path needs an explicit boundary that prevents an in-flight write from landing in the gap between the old and new state. Concretely: take a fresh, final backup/snapshot immediately before the real migration begins (the earlier clone can be stale by the time the actual migration runs, so it isn't a substitute for this), run the migration as backward-compatible expand-contract in small, monitored batches so a failure partway through never forces discarding already-migrated data, and gate the blue-to-green traffic cutover on replication lag explicitly: define a maximum acceptable lag (for example, hold the cutover while green's replica lag exceeds a few seconds) and only cut traffic over once green has caught up to that threshold, rather than cutting over on a fixed timer regardless of lag. During the cutover moment itself, a brief write-quiesce or dual-write window (writes are accepted by blue and also applied to or replicated into green before green starts serving reads) closes the specific gap where a write landing in the last moments before cutover could otherwise be lost to whichever side ends up not serving traffic.
- Rollback scope: if a problem emerges post-cutover, blue-green gives fast APPLICATION rollback (switch back to blue), which is why replication should keep flowing from green back to blue for a defined window after cutover, so blue doesn't fall behind and a same-day switch-back doesn't lose whatever writes landed on green in the meantime; but if the issue traces to the migration itself rather than the application code, that's a data-layer rollback with its own considerations (covered by the backward-compatibility discipline that made the migration safe to begin with), not something the blue-green switch alone fixes.
Worked example
A migration converting a JSON blob column into normalized relational fields: applied first to a read-only production clone, revealing that roughly 2% of real production rows have malformed JSON that the migration's parsing logic doesn't handle, a data-shape problem synthetic test fixtures never surfaced. The migration logic is fixed to handle that edge case, re-validated against the clone, and only THEN applied to the actual production database with the same batched, lag-monitored discipline: a fresh pre-migration backup is taken, batches are throttled to keep replica lag under a defined threshold, and the blue-to-green traffic cutover waits until that threshold is met, with a brief dual-write window bridging the cutover moment itself so no write is lost in the gap; the application's blue-green cutover happens afterward, once the schema itself is confirmed safely migrated.
Trade-offs and pitfalls
Cloning a large production database is itself a real operational cost (storage, time to create the clone, and it can go stale relative to live production if there's a meaningful delay between cloning and actually running the real migration), so this technique earns its cost specifically for migrations complex or risky enough that catching a data-shape problem before it hits live data is worth the overhead; a simple, well-understood migration probably doesn't need it. The common mistake is treating clone validation as a substitute for the real migration's own operational safety discipline (batching, lag monitoring, a fresh pre-cutover backup, and an explicit lag threshold gating cutover) rather than as a complementary, earlier-stage check.
What's the difference between a full rollback and a partial rollback? Give one concrete scenario where you'd choose each.
Sample Answer
Direct answer
A full rollback reverts every service or component involved in a release back to its previous version; a partial rollback reverts only some of them, leaving others on the new version. You choose partial when only some of the changed components are actually implicated in the problem and reverting the rest would be unnecessary churn or would itself introduce risk (for example, if a later component now depends on an earlier one's new behavior).
Structured elaboration
- When full rollback makes sense: a single service deployed a bad change, or several services were released together as one coupled unit where partial reversion would leave them in an inconsistent, untested combination.
- When partial makes sense: a coordinated multi-service release where only ONE service's new version is misbehaving, and the others are backward-compatible enough to keep running against either the old or new version of the problem service.
- Risk in partial rollback: you have to be confident the services you're leaving on the new version don't assume the now-reverted service's new behavior; if they do, a partial rollback trades one outage for a different, subtler one.
Worked example
Full: three tightly coupled microservices (auth, session, and API-gateway) deploy together as one release because the API gateway's new version assumes session's new token format; a bug anywhere means reverting all three together, since a partial revert would leave a token-format mismatch between them.
Partial: a release touches a recommendations service and a completely independent notifications service in the same deploy window purely for scheduling convenience; if only recommendations regresses, rolling back just that service and leaving notifications on its (unrelated, unaffected) new version is the right call, since reverting notifications too would be pure unnecessary churn.
Trade-offs and pitfalls
Partial rollback is faster and less disruptive when it's safe, but the judgment call of "is it actually safe to leave X on the new version" is exactly where mistakes happen; teams that don't explicitly map service dependencies before a coordinated release often discover the hard way, mid-incident, that two "independent" services weren't as independent as assumed.
Describe how GitOps changes the rollback model compared to imperative CI/CD. Explain concrete steps to revert a bad deployment using a GitOps workflow (for example ArgoCD or Flux) and how you ensure the cluster reconciles to the reverted state safely.
Sample Answer
Direct answer
Imperative CI/CD's rollback model is "run the DEPLOY step again with the previous artifact," a forward-facing operation on the deployment TOOL; GitOps's rollback model is "make Git say what it said before," a backward-facing operation on the SOURCE OF TRUTH, and the deployment mechanism (the reconciler) then does exactly what it always does, reconcile the cluster toward whatever Git currently declares. This is a genuine model shift, not just a different command: in GitOps, there is no separate "rollback" code path at all, reverting IS just another declarative change, going through the identical PR-and-reconcile flow as any other change, including review.
Structured elaboration
How GitOps changes the rollback model. Imperative CI/CD typically has rollback as its OWN distinct mechanism (a "redeploy previous version" pipeline action, sometimes with different permissions or a different code path than a normal deploy). GitOps has NO separate rollback mechanism; a revert is structurally identical to any other change, a Git commit that changes the declared state, reviewed the same way, applied by the same reconciliation loop. This means rollback INHERITS every safety property (review, audit trail, policy-as-code evaluation) normal changes get, rather than needing those properties separately re-implemented for a distinct rollback code path.
Concrete steps to revert a bad deployment.
- Identify the specific commit that introduced the bad change (using the same evidence-first approach any outage investigation follows).
git revert <bad-commit>(or, for a promotion-based flow, a new commit re-pinning the PREVIOUS artifact reference), creating a NEW commit undoing the change, never force-pushing over history.- This revert PR goes through the SAME review as any change (expedited under the emergency-change path if genuinely urgent, but still a real, recorded review, never skipped entirely).
- On merge, the GitOps controller (Argo CD or Flux) detects the Git state has changed and reconciles automatically, no separate "trigger a rollback" action needed beyond the normal merge that any change would need.
Ensuring the cluster reconciles to the reverted state safely. Adapting the same rollback caution used for Terraform to Kubernetes: confirm the revert's computed diff (what the reconciler will actually change) BEFORE merging, the same plan-review discipline applies here to a GitOps revert too, since "just revert the commit" does not guarantee the resulting change is itself risk-free (a revert can, in principle, trigger its own significant change if enough time and other changes have passed since the original commit). Once merged, monitor the reconciliation completing successfully, not just assume merge equals resolved, a revert PR can itself partially fail to apply, exactly like any other change.
Worked example
A bad deployment of checkout-api (a broken config value) via Argo CD:
Imperative CI/CD equivalent (for contrast): trigger the CI/CD pipeline's "redeploy previous version" action, which re-runs the deploy step against the prior artifact; this is a DIFFERENT operation from a normal deploy, often with its own separate permissions and, depending on the pipeline's design, potentially LESS reviewed than a normal deploy would be (rollback is often treated as an emergency action that skips normal process).
GitOps:
git log --oneline apps/checkout-api/overlays/prod/ # identify the bad commit
git revert <bad-commit-sha> # creates a new, reviewable commit
git push # opens/updates the PR
# PR reviewed like any other change (or expedited via the emergency path)
# on merge: Argo CD's application-controller detects the Git change,
# computes the diff, and reconciles automatically
argocd app get checkout-api # confirm sync completed successfully
No separate "rollback" button or pipeline stage exists; the revert PR IS the rollback, reviewed and applied through the identical mechanism every other change uses.
Trade-offs and pitfalls
- Common mistake: treating a GitOps revert as automatically risk-free just because "it's just undoing something." As with a Terraform rollback, a revert's ACTUAL computed effect depends on current state, which may have moved since the original bad commit; skipping the normal plan/diff review specifically because "it's a rollback" is exactly backwards, a rollback deserves the SAME scrutiny as any change, not less.
- Imperative CI/CD's separate rollback mechanism is sometimes FASTER in the moment (a single button/command) but usually LESS reviewed, a real trade-off worth naming explicitly rather than assuming GitOps's model is strictly superior in every dimension; GitOps's rollback is slower by exactly the amount of review it goes through, which is the SAME safety property normal changes get, not an accident.
- A revert PR can itself partially fail to apply, exactly like any other forward change can, treating "the revert PR merged" as equivalent to "the rollback completed" skips confirming the reconciliation itself actually succeeded, a real, easy-to-skip verification step.
- The emergency-change path is available for a GENUINELY urgent rollback, but even that path preserves SOME review (a lightweight, in-the-moment approval) and a mandatory retroactive capture; it is a faster lane through the same fundamental model, not an exception that abandons GitOps's core review-everything property entirely.
Unlock Full Question Bank
Get access to all Safe Deployment and Rollback Strategies interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.