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.
Describe how to implement a canary deployment using only native Kubernetes primitives (no service mesh): creating and controlling the canary ReplicaSet, shifting traffic gradually, evaluating metrics, and automating promotion or rollback.
Sample Answer
Direct answer
Without a service mesh, a canary on native Kubernetes primitives means running a SEPARATE, smaller ReplicaSet for the canary version, sharing the same Service (and therefore the same label selector) as the stable ReplicaSet, and controlling the traffic split purely by the RATIO of canary-to-stable pod counts, since without a mesh there's no fine-grained percentage-based routing, only "however many pods exist, roughly that share of traffic."
Structured elaboration
- Two ReplicaSets, one Service: the stable Deployment/ReplicaSet and a separate canary ReplicaSet both carry a label the shared Service selects on (e.g.
app: checkout-api), so Kubernetes' built-in load-balancing (round-robin across matching endpoints) sends traffic to both, with the SPLIT determined by relative pod count, not an explicit percentage. - Shifting traffic gradually: scale the canary ReplicaSet up (and optionally scale stable down proportionally) in steps, for example canary at 1 of 100 total pods (~1%), then 5 of 100 (~5%), then 25, then fully replacing stable. This is coarser-grained than a mesh's weighted routing (you're limited by pod-count granularity, especially at low replica counts) but requires no additional infrastructure.
- Evaluating metrics: since both versions share one Service, you need the canary pods separately LABELED and QUERYABLE (a
version: canarylabel alongside the sharedapplabel) so your metrics system can filter and compare canary-specific metrics against stable, even though both are receiving traffic through the same Service. - Automating promotion/rollback: a script or controller that watches the canary-specific metrics, and on a pass, scales the canary ReplicaSet up (and stable down) to the next step; on a fail, scales canary back to zero and stable back to full, reverting via the same ReplicaSet-scaling mechanism.
Worked example
At 100 total desired pods: canary starts at 1 replica (stable at 99), giving roughly 1% of traffic via Kubernetes' round-robin balancing across matching endpoints. After a clean observation window, canary scales to 5 (stable to 95), then 25/75, then finally canary fully replaces stable (canary scales to 100, stable to 0, and the canary Deployment is effectively promoted to become the new "stable").
Trade-offs and pitfalls
The coarse granularity is the real limitation: at low total replica counts, you can't achieve a genuinely fine percentage (with 10 total pods, the smallest non-zero canary slice is 10%, not 1%), and Kubernetes' round-robin isn't a precise, deterministic percentage split the way a mesh's weighted routing is, it's a rough approximation based on endpoint count. This approach is a reasonable, infrastructure-light starting point for teams without a service mesh, but the imprecision and lack of built-in session affinity or fine-grained routing rules are exactly what a mesh (or a load-balancer with native weighted routing, like an ALB) is built to solve properly.
Design a progressive-delivery ramp for a payment service: an initial 1% canary, ramp to 50% over two hours if clean, then 100% after 24 hours. What automation and metric checks run at each stage, and how do you handle a partial rollback if problems appear at the 50% stage?
Sample Answer
Direct answer
A progressive-delivery ramp for a payment service needs the automation to actively gate each stage's advance on real metric checks, not just wait out a timer, and the partial-rollback plan for the 50% stage needs to distinguish cleanly between requests that already went through the new code (which may have real side effects, like a payment already processed) and requests still ahead of the rollback taking effect.
Structured elaboration
- 1% canary: the smallest, most cautious stage, watched closely with a shorter observation window since the blast radius is tiny; metric checks focus on error rate and latency deltas against the stable baseline, plus a payment-specific correctness signal (successful-transaction rate, any reconciliation mismatch) since a payment service's most dangerous bugs may not show up as a raw HTTP error at all.
- Ramp to 50% over two hours if clean: this isn't a single jump, it's itself a staged ramp (say 1% -> 10% -> 25% -> 50%, each requiring its own clean metric window before advancing), automated so a human doesn't have to manually approve every micro-step, but with metric checks gating EVERY step, not just the final 50% checkpoint.
- 100% after 24 hours: a long hold at 50% specifically to accumulate enough transaction volume and TIME (payment issues can be slow-building, like a subtle reconciliation drift that only shows up after a batch settlement process runs) before committing to full exposure.
- Partial rollback if problems appear at 50%: reduce the new version's traffic share back down (not necessarily to zero immediately, potentially stepping back to a smaller, still-nonzero percentage to keep gathering diagnostic data on a contained population while you investigate), while the ALREADY-PROCESSED transactions on the new code path need their own review: were any payments processed incorrectly, and do they need a compensating action (a reversal, a manual reconciliation) distinct from the traffic-routing rollback itself?
Worked example
At the 50% stage, an automated check flags a reconciliation discrepancy in a batch of transactions processed by the new code. The traffic-routing rollback (scaling the new version's share back to 5%, not necessarily zero, to preserve some live diagnostic signal) happens within minutes via the automated pipeline. Separately and on a different timeline, a manual reconciliation process reviews every transaction that went through the new code path during its exposure window to determine whether any need a compensating correction, since simply routing future traffic away doesn't undo whatever the already-processed transactions did.
Trade-offs and pitfalls
Payment services are the canonical example of where "roll back the traffic" and "the problem is fixed" are NOT the same thing, since money may have already moved; the automation needs to be scoped clearly to what it CAN fix (stop MORE transactions from hitting the bad path) while explicitly flagging what it can't (undo transactions that already happened), which needs a human-driven reconciliation process rather than being folded into the automated rollback itself.
You operate a global service and want to do region-by-region staged rollouts to limit blast radius. How would you coordinate DNS, geo-routing, and multi-region orchestration, and what would you test before each region's rollout?
Sample Answer
Direct answer
Region-by-region staged rollout needs to coordinate the DNS/geo-routing layer that sends users to a region with the actual capacity and readiness of that region's new deployment, so a region only starts receiving live traffic on the new version once it's been independently validated, not just because the calendar step says "now roll out region 2."
Structured elaboration
- Deploy without exposing: roll the new version out to a region's infrastructure first WITHOUT shifting user traffic there yet, so you can validate it against synthetic or internal traffic before any real user in that region is affected.
- Geo-routing shift: use DNS-based geo-routing (with a suitably short TTL for the specific rollout window) or a global load balancer's region-weighting to gradually shift REAL user traffic for that region onto the newly-validated deployment, rather than an instant full cutover.
- Per-region validation before advancing: confirm the region's health (error rate, latency, any region-specific business metric) independently before starting the NEXT region's rollout; a region's traffic pattern, data-residency constraints, or infrastructure quirks can surface a bug that a different region's rollout wouldn't have caught.
- What to test before each region's rollout: region-specific configuration (any locale, currency, or regulatory-specific behavior), the region's actual infrastructure capacity for the new version's resource profile (a region with older or smaller instance types might not handle the same load the way a larger region does), and connectivity to any region-local dependencies (a regional database replica, a regional cache) that a different region's testing wouldn't have exercised.
- Order regions by risk: start with a lower-traffic or lower-stakes region rather than your largest market, so a regional-specific bug is caught on a smaller blast radius before reaching your highest-value region.
Worked example
A four-region service rolls out to its smallest region first (validated internally, then geo-routed traffic shifted over 24 hours while watching region-specific metrics), then the next-smallest, and so on, saving the largest region for last once the release has already accumulated real-world validation from three smaller regions. If the second region reveals a regulatory-specific data-handling bug unique to that region's compliance requirements, the rollout pauses there rather than proceeding to region three until it's fixed and re-validated, and regions one and two's rollout status is unaffected since they're independently tracked.
Trade-offs and pitfalls
This is meaningfully slower than a global simultaneous rollout, trading time for the ability to catch region-specific issues on a contained blast radius; the DNS-TTL consideration matters concretely, since a long cached TTL from a previous, unrelated DNS configuration can mean some users' geo-routing doesn't actually update as fast as the rollout plan assumes, so validating actual traffic-shift behavior (not just assuming DNS changes take effect instantly) is an important, easy-to-skip step.
Design a GitOps operator that can perform atomic multi‑service deployments based on a dependency graph: when a change touches multiple services, the operator must reconcile all manifests and ensure either all succeed or a safe rollback occurs across services. Describe the data model, reconciliation loop, handling of partial failures, and rollback/compensation semantics.
Sample Answer
Direct answer
Atomic multi-service reconciliation over a dependency graph needs the SAME "all succeed or safe rollback" guarantee a database transaction provides, but GitOps has no equivalent of a database's native transaction mechanism, each service's manifests apply independently through the underlying Kubernetes API, so the operator has to construct that guarantee itself: track each service's individual reconciliation status against the dependency graph's required ORDER, and on ANY service's failure, actively COMPENSATE (roll back) every service that had already succeeded in this same multi-service change, rather than leaving a partially-applied graph in an inconsistent state.
Structured elaboration
Data model. A MultiServiceChange custom resource capturing: the SET of services involved in this specific coordinated change, their DEPENDENCY ORDER (a directed acyclic graph, service B cannot reconcile until service A, which it depends on, has succeeded), each service's OWN manifest reference (a Git commit/digest), and, critically, each service's PRIOR successful state (the last known-good manifest reference for that service, needed as the compensation target if a rollback becomes necessary).
Reconciliation loop. Processes services in DEPENDENCY ORDER (a topological sort of the graph), reconciling each only once its dependencies have themselves reached a succeeded state; this is the mechanism that gives ordering guarantees a plain, independent per-service reconciliation loop does not provide on its own.
Handling of partial failures. If a service in the middle of the ordered sequence FAILS to reconcile (after its own bounded retry), the operator does NOT continue reconciling the REMAINING, not-yet-processed services in the graph (since they may depend on the failed one, and even if they don't directly, the overall multi-service change is now incomplete); it transitions the MultiServiceChange to a compensating state and begins rollback.
Rollback/compensation semantics. Roll back every service that ALREADY succeeded in THIS multi-service change, in REVERSE dependency order (a service's dependents must be rolled back before the service itself, mirroring the forward order's own logic), reverting each to its recorded PRIOR successful state (not simply "delete," since the prior state may itself be a specific, meaningful configuration, not merely "nothing"); the never-reconciled remaining services in the graph need no compensation at all, since they were never actually changed.
Worked example
A MultiServiceChange spanning three services with dependency order network-policy before auth-service before checkout-service (checkout depends on auth, auth depends on the network policy being in place first):
network-policyreconciles successfully first (no dependencies).auth-servicereconciles successfully second (its dependency, network-policy, already succeeded).checkout-serviceFAILS to reconcile (its new manifest references a config value that does not exist yet).- The operator transitions to
compensating: rolls backauth-serviceto its PRIOR successful manifest first (checkout, the failed one, was never actually applied, so it needs no rollback, just needs to stop being retried), then rolls backnetwork-policyto its prior state. - Final state: all three services back at their PRE-CHANGE configuration, a clean, fully-compensated failure, rather than network-policy and auth-service left on their NEW configuration while checkout alone failed, which would have been a genuinely inconsistent, partially-migrated state.
Trade-offs and pitfalls
- Common mistake: rolling back services in the SAME order they were applied, rather than REVERSE dependency order. Per the worked example, rolling back
network-policy(whichauth-servicedepends on) WHILEauth-serviceis still running its new configuration risksauth-serviceoperating against a network policy that no longer matches what it expects, briefly recreating the exact kind of inconsistency the whole compensation mechanism exists to avoid; reverse-order rollback (dependents first, dependencies last) is what keeps every INTERMEDIATE state during the rollback itself consistent too, not just the final state. - "Roll back to the prior successful state" requires that prior state to have actually been RECORDED before the new change began, an operator that only tracks the CURRENT desired state, with no memory of what preceded it, cannot perform this rollback at all; this is a real, easy-to-omit data-model requirement, not an implementation detail.
- A service that was never reached in the forward pass (because an earlier dependency failed first) needs NO compensation, per the worked example's
checkout-service, attempting to "roll back" a service that was never actually changed is at best a wasted no-op and at worst risks touching a resource the operator has no legitimate reason to be modifying right now. - This entire mechanism assumes the dependency graph itself is ACCURATE and complete: a graph missing a real dependency risks reconciling (or worse, considering "successful") a service whose actual prerequisite was never satisfied, defeating the ordering guarantee the whole design exists to provide.
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.