Case study

Migration recovery for a B2B SaaS platform

A cloud migration completed on schedule, but stability got worse. Incidents increased, delivery slowed, and the team lost confidence in the platform.

Failure signals
  • Routing and latency inconsistencies after the move.
  • Permissions drift and unclear ownership boundaries.
  • State and config changes done manually to survive.
  • Deployments became risky and unpredictable.
Engagement readout

The work started with one question: what is actually breaking the revenue path?

This was not a migration retrospective. It was a recovery engagement built to stop repeat instability without forcing a second re-platform.

Migration blast radius map
Migration dependency and blast-radius visual

The first retained view tied routing, identity, and runtime dependencies to concrete failure paths so the team could stop guessing which post-migration issues were actually coupled.

The platform team did not need abstract "optimization." They needed a bounded recovery sequence that contained live risk first, then restored predictable delivery.

Week 1
Freeze unsafe routing and config drift.
Week 2
Normalize ownership and cut legacy traffic paths.
Outcome
Stability returned without another broad rework cycle.
Routing risk mapNamed the dependency chain behind the post-cutover latency and checkout failures.
Recovery sequenceOrdered the fixes so traffic and identity issues were contained before optimization work started.
Owner handoffClarified who owned ingress, runtime config, and service-boundary follow-through after stabilization.
What changed first

Contain, trace, then repair

The sequence mattered. The job was to stop more risky changes from landing while the actual failure chain was mapped.

First 72 hours

Freeze unsafe deploy changes, isolate the unstable traffic path, and reconcile the highest-risk permission and runtime overrides.

Next 2 weeks

Remove the legacy route coupling, normalize ingress behavior, and restore a release path that did not depend on tribal knowledge.

Context

Growth-stage SaaS platform, lean platform team, minimal downtime tolerance, and no appetite for another full redesign.

Success criteria

Critical-path latency stabilized, rollback confidence restored, and ownership became clear enough for the internal team to operate safely.

Concrete outputs

What the team kept after the recovery work

The retained assets were designed to survive after the immediate incident pressure dropped.

Failure chain excerpt
Symptom: latency spikes after cutover
Root cause: mixed ingress + legacy route ownership
Impact: checkout errors during peak demand
First fix: normalize ingress + remove legacy fallback
Guardrail: release gating + owner sign-off

Why this was useful internally

  • Business impact was tied to named technical causes, not vague migration anxiety.
  • The platform team had a safe sequence instead of parallel uncoordinated fixes.
  • Ownership boundaries were explicit enough for follow-through after handoff.
Technical evidence

Verifying a post-migration cluster nobody could vouch for

Anonymized specifics from a real engagement. Included because the useful part of a migration readout is what was actually checked.

“Production is fine” with nothing behind it

The situation. A Kubernetes cluster running roughly nine microservices came back online after a billing pause and a node recycle. The client’s position was that production was healthy. Nobody could produce anything to support that, and the platform had already been through a migration from self-managed Kubernetes to a managed service.

What was actually wrong. Nothing was on fire, and that is the part worth writing up: the claim was unverified, not false. There was no evidence either way, and after a node recycle that is a real risk, because pods reschedule, volumes rebind, certificates approach expiry and controllers restart without anyone watching. The audit did surface one genuine anomaly. The cloud provider’s own cloud-controller-manager and CSI controller Deployments were not present where they should have been. On a managed Kubernetes service those are normally provider-managed, so their absence is either a serious fault or an artifact of the provider running them outside user-visible namespaces.

What I changed. Rather than guess, I audited the running cluster layer by layer: nodes and their pressure signals, kube-system components, ingress, cert-manager, the GitOps applications, storage and persistent volume claims, and the workloads. On the missing controllers I reasoned it out instead of escalating: load balancers were provisioning, volumes were attaching and ingress was routing, and those three facts can only be true if the controllers are running somewhere. The correct conclusion was that the provider hides them, and the correct action was to record it and move on rather than manufacture an incident.

The result. A written health report the client could hand to someone. Three worker nodes Ready with no memory, disk, PID or network pressure, and node memory between roughly 52% and 73%. Cilium CNI running 3/3. CoreDNS 2/2 with seven restarts about fifteen hours earlier, consistent with the node recycle rather than a fault. Ingress-NGINX healthy at 2/2 available. TLS certificates valid, persistent volume claims bound with no data loss, GitOps applications synced.

And the gaps, named rather than smoothed over: no resource guardrails, no external uptime probes, no PodDisruptionBudgets. The client got a defensible answer instead of a shrug, in both directions.