Moving a live product from AWS to GCP is not like reading documentation and clicking a “migrate” button. It feels like replacing the engine of a car speeding down the highway: customers still need to make purchases, orders still need to be processed, and everything underneath is being taken apart and put back together day by day.
The original article on madhav.dev recounts that exact experience. And the most valuable takeaway isn't the list of which AWS services map to which GCP services, but rather the question: if I could do it again, what would I do differently? Here is what I learned, written with a practical approach so you can take it back to your team this week.
Cloud migration is rarely purely a technical problem
Most engineers envision migration as a matter of APIs, CLIs, Terraform providers, reading docs for a few sessions, and calling it a day. But when the actual work begins, most of the time is spent on questions not found in any documentation: who is responsible when a new quota is hit? Which internal service is secretly calling the old endpoint? Does accounting understand how the new provider calculates bills?
Therefore, before opening your editor, write down the reasons for migration on paper and be very specific. If the reason is simply “to modernize,” you will regret it by the second month. If the reason is cost, check where that cost actually comes from. If the reason is team capability, ask whether the team truly wants to learn a new toolset.
A good migration is not the fastest one, but the one you can still justify to yourself after six months.
Do not lift and shift your old architecture into a new home
The quickest way to “finish the job” is lift and shift: recreating each virtual machine, each bucket, and each queue exactly as they were on the old platform. It works, but you have just brought all your technical debt to a new place where pricing models and operations are completely different. You will pay for it with the exact things you thought you left behind.
Take the time to review the following points before copying configurations:
- Service boundaries: where you should use managed services versus keeping things self-managed to control costs and latency.
- Autoscaling assumptions: scaling behavior and startup times are not identical across platforms, so do not assume they are the same.
- Cross-region data flows: your old design may inadvertently create continuous cross-region traffic without you realizing it.
- Code snippets that “only run on the old provider”: SDKs, metadata services, log formats, authentication mechanisms. This is where the most time is spent.
A small but effective trick: redraw the architecture on a blank sheet of paper without looking at the old diagrams. If you cannot explain why a component exists, chances are it no longer needs to exist.
Understand billing before writing your first line of code
Cloud costs are the easiest thing to surprise you during a migration. For the exact same workload, two providers can yield very different bills because their unit metrics differ: egress traffic, cross-region traffic, request counts, storage classes, and the lifecycle of temporary data.
Do not wait until the end of the month to look at the bill. Do these three things right from the start:
- Estimate costs for network traffic as a separate category, as this is the most frequently missed expense.
- Enable labels for every resource upon creation, categorized by service and environment, so you can track where money goes later.
- Set budget alerts below expected thresholds to learn early rather than learn expensively.
Once you see money flowing by service, you will have more reasons to clean up redundant components — and that is usually the largest saving of the entire project.
Identity, access, and networking must be done first, not last
This is the most painful lesson. Account structures, permission models, and project organizations on the new provider often do not map one-to-one with the old ones. If you build the application first and think about permissions later, you will have to revise almost everything: environment variables, secret paths, access policies, and how services call one another.
The more logical order is the reverse: design the organizational hierarchy, separate environments, set up network rules and authentication flows first, and only then deploy applications on top. It sounds slow, but it saves you weeks of fixing permission errors in the middle of the night.
- Clearly separate development, staging, and production environments from the very first framework.
- Never use personal accounts for any automated workflows.
- Record all infrastructure changes as code so you can rebuild from scratch when needed.
Data, downtime, and the rollback plan
The hardest part of any migration is not building new infrastructure, but transferring data without corrupting numbers. Users do not care which service you use; they only care if their orders are still correct.
Therefore, design your rollback plan before cutting over. Run both systems in parallel for a period, write data to both places if necessary, and most importantly, have a reconciliation method to verify whether new data matches old data. A simple daily reconciliation checklist will save you from countless stressful meetings.
And give stakeholders a heads-up about the possibility of brief disruptions. Staying silent and letting the system run slowly for a few minutes is much worse than a prior notice.
People learn new tools slower than you think
Engineering may finish in a few weeks, but team reflexes take months. When an incident occurs at 2 AM, the on-call engineer will fall back on old instincts: finding old dashboards, typing old commands, opening old runbooks. If those things are no longer valid, troubleshooting time will be much longer than usual.
Invest in things that seem secondary: rewriting runbooks, naming resources clearly, pinning essential dashboards in one place, and running a small rehearsal so the team can practice operating the new system together. Do not turn the migration into a solo project for your most talented person — turn it into shared knowledge.
If I could do it again, I would follow this order
- Clearly write down the reasons for migration and success criteria in undeniable terms.
- Redesign the architecture on a blank sheet of paper, keeping only what is truly necessary.
- Build the permissions, networking, and environment frameworks before introducing applications.
- Migrate a small, low-risk service to learn how operations actually work.
- Establish cost measurement and data reconciliation from day one.
- Only cut over when a rollback plan is in place and rehearsed at least once.
The interesting thing is that after all is said and done, many teams realize the migration didn't make them smarter about the cloud, but rather helped them understand their own systems better. You are forced to answer questions you might have previously avoided: what is this component for, who calls it, and what happens if it dies? That is the unspoken reward, and also the most durable one.
As for you, if you are facing a migration, try a small test: write down three things you would do differently compared to last time. If you cannot write down three, perhaps you do not need a migration yet — you need a cleanup first.
What do you think: for a smoothly running system, should you migrate to the cloud just because invoices aren't optimized yet, or should you optimize first before moving houses? Leave your perspective in the comments — I'd love to hear the counter-perspective. And if this article makes your migration journey feel a bit lighter, save it and send it to a colleague preparing for a similar project.

0 Comment
Add your comment to this article