1
0 Comments

Why Finishing a Migration Is Harder Than Starting One: A Conversation with Vishal Shah

Enterprises have spent a decade moving to the cloud, and they are still bad at finishing.

Enterprises have spent a decade moving to the cloud, and they are still bad at finishing. Even now, 38% of cloud migrations run over budget and 31% miss their timeline, with the complexity of legacy systems the single biggest cause, and nearly two-thirds of organizations say that moving to the cloud actually increased the number of incidents they have to deal with. Nowhere are the stakes higher than in banking, where the system being modernized may be deciding, in milliseconds, whether tens of millions of payments a day are allowed to move. Get that migration wrong, and the consequence is measured in money that does not move.

Vishal Shah has spent 22 years on the hard end of that problem. A senior engineering manager who has led large-scale cloud modernization programs across enterprise systems, he most recently led the rebuild of the permissions, limits, and decisioning layer of a top U.S. bank's payments platform, the machinery that decides who is allowed to move money and how much, across instant payments, bill pay, and bank transfers. He led a global team of more than 50 engineers, on a system that evaluates tens of millions of transactions every day. His focus is the part of modernization that never makes the launch announcement: what it takes to actually finish.

We spoke with Vishal about why going live is the easy part, how AI is changing the way mission-critical software gets built, and what most enterprises still get wrong about migrating systems that cannot afford to break.

Modernizing a bank's core payment systems sounds straightforward on a slide. Why is it so hard in practice?

Because you are changing the engine of the plane while it is flying, and the plane is carrying money. For this platform, planned downtime was generally unacceptable, and the services were designed for continuous availability. It runs every second of every day, and the moment a decision takes too long or comes back wrong, someone's rent payment fails or a fraudulent transfer goes through. The system I worked on decides, in real time, whether a payment is permitted and whether it sits within a customer's limits, across several payment rails at once. All of that has to happen in milliseconds, at a scale of tens of millions of decisions a day, with no tolerance for being down.

So the difficulty is never really the new technology. Building the new service is the fun part. The hard part is that a legacy system this critical has decades of accumulated logic, edge cases, and quiet dependencies that nobody fully remembers, and you have to reproduce all of it perfectly while improving it, without ever taking the old one offline until you are certain. You are not building on an empty lot. You are rebuilding a house someone is still living in, one room at a time, without turning off the water.

Walk me through what you actually built.

We rebuilt the permissions and limits layer as a set of cloud-native microservices, cleanly separated by domain, so the rules governing who can pay whom and how much, lived in well-defined services instead of a tangle of legacy code. The core challenge was latency at scale. A real-time payment decision cannot wait, so we designed the whole system around fast, cached access to the data those decisions depend on, backed by a highly available data store, with event-driven workflows keeping everything consistent across the different rails. We designed the APIs so retried requests could be processed without duplicating the payment operation. Distributed tracing and structured logging help us trace the request path and investigate what happened .

The result was a platform that could support tens of millions of daily payment decisions at consistently low latency, and one we could actually extend. Throughput capacity went up 20–30% through better caching and concurrency. Just as important, adding a new payment feature stopped being a research project, because we had built reusable templates and shared components instead of one-off integrations. In a bank, that speed-to-market is the difference between shipping a capability this quarter and shipping it next year.

You've written publicly about the nuts and bolts of this. Which technical choices matter most?

Containers, done properly, are what make this kind of modernization sustainable. I wrote a piece walking through deploying a Spring Boot microservice on AWS Fargate, because the deployment model is where a lot of teams quietly lose ground. If every service is packaged the same way and deployed the same way, with the infrastructure managed for you, your engineers spend their time on payment logic instead of babysitting servers. Standardizing that pattern across an organization is boring, and it is exactly what lets 50 engineers work on one platform without stepping on each other.

The other choice that matters is treating consistency as a first-class problem. In payments, the same request can arrive twice because a network blipped, and if your system is not idempotent, you have just moved money twice. So much of the architecture is about guaranteeing that operations are safe to repeat, that state stays synchronized across rails, and that failures degrade gracefully instead of cascading. None of that shows up in a demo. All of it shows up at 2 a.m. during a peak load event, which is the only test that counts.

You've argued that launching a new platform and completing a migration are not the same thing. What do people miss?

This is the one I feel strongest about. I wrote a whole piece on how a new platform going live is not the same as completing a migration, because the industry keeps confusing the two. Going live means the new system is handling traffic. Completing the migration means the old system is gone, every consumer has moved, every edge case is handled, and you have switched the legacy one off for good. The gap between those two states is where projects die. [56% of cloud migrations run into unforeseen technical debt](https://wifitalents.com/cloud-migration-failure-statistics/) that keeps them from ever realizing the benefits, and most of that debt lives in exactly that gap.

What happens in practice is that a team launches the shiny new platform, declares victory, and then quietly keeps the legacy system running because a handful of obscure flows never got moved. Now you are paying to run both, maintaining two systems, and carrying all the risk you were trying to retire. Finishing means being ruthless about that long tail: hunting down every last consumer of the old system, migrating the ugly edge cases nobody wants to touch, running old and new side by side until the numbers match exactly, and only then decommissioning. It is unglamorous, it is political, and it is the entire point. A migration you did not finish is just a second system you now have to maintain.

You introduced AI-assisted engineering into a regulated, mission-critical environment. How did that go?

Carefully, which is the only way it works in a bank. We brought in an AI coding agent to handle the repetitive parts of building these services: scaffolding, boilerplate, first-draft documentation, and test generation. The productivity gain is real. A study of 4,800 enterprise developers found that those using an AI assistant [completed 26% more tasks without a statistically significant overall decline in build-success rate](https://itrevolution.com/articles/new-research-reveals-ai-coding-assistants-boost-developer-productivity-by-26-what-it-leaders-need-to-know/), and we saw our engineering cycle time fall by about 25% across the payments organization. When a large share of a service is predictable structure, letting a machine draft it and having an engineer review it is simply faster.

But the review is not optional, and that is the part teams get wrong. AI-generated code in a payment system gets the same scrutiny as code from a brand-new hire, because a plausible-looking mistake in a permissions rule is a security incident, not a bug. So we put governance around it. The AI drafts, humans own the decision, and every change goes through the same controls, testing, and audit trail that regulators expect. Used that way, AI takes repetitive work off engineers' plates so their judgment goes where it actually matters, which is the logic that decides whether money is allowed to move.

Across all the modernization work you've led, what is the mistake you see enterprises make again and again?

Treating migration as a technology project when it is really an organizational one. The tooling for containers, orchestration, and cloud data is mature now. It is not what stops you. What stops you is that finishing a migration requires dozens of teams to agree on contracts, retire systems they are attached to, and do the tedious work of moving the last 10% after the exciting 90% is already live. Nobody gets promoted for decommissioning a legacy service, so it does not happen, and the organization ends up permanently straddling old and new.

The second mistake is underestimating the legacy system. That old rule engine everyone wants to replace encodes 20 years of hard-won lessons, most of them undocumented, many of them written the day after something went wrong. If you rebuild without understanding why each of those rules exists, you will faithfully reproduce the gaps and rediscover the same failures the hard way. The respect you pay the old system is what decides whether the new one survives contact with reality.

Where does this kind of work go next?

Toward systems that are built, from the start, to be replaced. The lesson of every hard migration is that the pain came from systems that assumed they would live forever and were never designed to be handed off. So the work now is architecting for the next migration before you need it: clean domain boundaries, explicit contracts, and observability deep enough that a future team can understand the system without the person who built it. You are trying to make sure the thing you ship today is not the legacy nightmare somebody curses in 10 years.

AI is going to push that further, because it changes the economics of the tedious work. A lot of the work that causes migrations to stall, boilerplate, test generation, and documentation, is exactly what these tools are good at, as long as you keep humans firmly in control of the decisions. I am less interested in AI writing clever code than in AI finally making it cheap to finish things properly. In this industry, finishing properly is the whole game. Everything else is just going live.