Every AWS migration starts with the same optimism: “We’ll move everything in 6 weeks, save 40% on infra, and never think about servers again.”
Then reality hits.
I’ve delivered 50+ AWS migrations over the past 5 years – from 3-person startups on Heroku to 200-person companies running bare metal in colocation. The technology isn’t what trips people up. It’s the decisions made (or not made) in the first two weeks that determine whether a migration finishes on time or drags on for months.
Here are the patterns I keep seeing.
1. You don’t have a migration strategy. You have a vague plan to “move stuff.”
AWS defines 7 migration strategies (the 7Rs): Retire, Retain, Rehost, Relocate, Replatform, Refactor, Repurchase. Most teams I work with initially want to “just move to AWS” without deciding which strategy applies to which workload.
The result: everything gets treated as lift-and-shift (rehost), even applications that should be retired or replaced with SaaS. Three months later you’re running the same inefficient architecture on EC2 instead of your old servers, paying more, and wondering what went wrong.
What works: Spend 3-5 days classifying every workload into a strategy before touching any infrastructure. In a typical 20-app portfolio, I usually see:
- 3-4 applications that should be retired (nobody uses them)
- 5-7 that can be rehosted (internal tools, legacy systems with short remaining lifespan)
- 5-8 that benefit from replatforming (swap self-hosted DB for RDS, cron for Lambda)
- 2-3 that genuinely need re-architecting (your core product)
This classification saves months of work on things that shouldn’t exist in the first place.
2. Data transfer costs are invisible until the first bill arrives
Here’s the conversation I have on almost every project:
“Why is our AWS bill $3,000/month higher than the estimate?”
“Your applications talk to services still running on-premise. Every API call crosses the internet. That’s egress at $0.09/GB.”
Cross-AZ traffic ($0.01/GB each direction), NAT Gateway processing ($0.045/GB), and egress to the internet – these costs don’t show up in the AWS pricing calculator because they depend on traffic patterns you haven’t measured yet.
What works: During your PoC phase, run actual traffic through the target architecture for 1-2 weeks. Measure real egress. Then multiply by 12. If the number surprises you, architect around it:
- VPC endpoints for S3 and DynamoDB (eliminates NAT Gateway charges for AWS service traffic)
- Keep services that talk to each other frequently in the same AZ
- If you’re communicating with on-premise systems, calculate whether AWS Direct Connect ($200-500/month) pays for itself vs. internet egress
I wrote a detailed cost breakdown for each AWS component if you want to model this before starting.
3. “We’ll fix security later” is the most expensive sentence in cloud
I’ve seen teams deploy production workloads with:
- Security groups open to 0.0.0.0/0 on all ports
- IAM policies with
"Effect": "Allow", "Action": "*", "Resource": "*" - Unencrypted RDS instances storing PII
- No CloudTrail, no GuardDuty, no audit trail
They always plan to “tighten it up after migration.” They never do – until an auditor or a breach forces the issue. Retrofitting security is 3-5x more expensive than building it correctly from day one, because by then you have applications depending on the misconfiguration.
What works: Define your security baseline before migrating the first workload:
# Terraform - enforce encryption on all RDS instances
resource "aws_db_instance" "main" {
# ...
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
# Never do this in production:
# publicly_accessible = true
publicly_accessible = false
}
Enter fullscreen mode Exit fullscreen mode
Enable CloudTrail, GuardDuty, and Security Hub on day one. They cost almost nothing compared to the alternative.
4. Big-bang migrations always fail in the same way
Team decides to migrate everything over a single weekend. Friday evening: “Here we go.” Saturday 3 AM: the database migration hangs at 73%. Sunday: half the team is debugging DNS while the other half discovers that the payment service depends on an internal API that hasn’t been migrated yet. Monday: rollback.
I’ve watched this happen three times. The problem isn’t technical competence – it’s blast radius. When everything moves at once, a single failure cascades through dependencies you didn’t map.
What works: Migrate in waves:
- Wave 0: Non-critical internal tools (low risk, builds team confidence)
- Wave 1: Stateless services (API gateways, frontend)
- Wave 2: Stateful services (databases, queues)
- Wave 3: Core business logic (payment, auth)
Each wave has a defined rollback plan. Each wave reveals patterns you’ll reuse in the next. The total calendar time is longer, but the actual time-to-completion is shorter because you’re not spending weekends recovering from cascading failures.
5. You’re treating AWS like a data centre
The classic anti-pattern: team migrates to AWS, deploys everything on oversized EC2 instances that run 24/7, never configures auto-scaling, and ends up paying more than on-premise.
Congratulations – you’ve built a very expensive data centre with a nicer console.
What works:
- Right-size from day one. Start with the smallest instance that passes your load tests, not the largest that “feels safe.”
- Schedule non-production environments. Dev and staging don’t need to run at 3 AM. Shutting them down outside business hours saves 65%.
- Use Savings Plans for baseline load. If you know you’ll always need at least 10 vCPUs of Fargate, buy a Compute Savings Plan for that baseline. You’ll save 30-40% vs. on-demand.
- Auto-scale everything. ECS services, EC2 Auto Scaling Groups, RDS read replicas. If it can scale, it should.
6. The PoC that wasn’t
Some teams skip the Proof of Concept and go straight from assessment to building production infrastructure. Their reasoning: “We know what architecture we want, why prototype it first?”
Then they discover in month 3 that:
- Aurora’s connection limit doesn’t work with their connection pooling approach
- Cross-region latency breaks their real-time collaboration feature
- The CI/CD pipeline takes 45 minutes because they chose the wrong build instance type
A 1-2 week PoC with your key workload would have surfaced all of these for the cost of a few hundred dollars in AWS charges.
What works: Build a throwaway prototype of your most complex workload. Not a production-ready environment – a minimal version that exercises the critical path: compute, database, networking, deployment. Measure performance. Measure cost. Then throw it away and build the real thing informed by actual data.
7. Nobody owns the cloud after migration
The migration team delivers, high-fives all around, and… who maintains this now?
If the answer is “we’ll figure it out,” you’ll figure it out at 2 AM when the first production incident hits and nobody knows how to read CloudWatch dashboards or interpret ECS task failures.
What works: Define the operating model before migration:
- Who gets paged when something breaks?
- Who approves infrastructure changes?
- Who reviews the AWS bill monthly?
- Where are the runbooks for common failures?
The migration is a project. What comes after is an operating model. Plan for both.
The pattern underneath all of these
Every one of these mistakes comes from the same root cause: optimising for speed of starting over speed of finishing.
Teams that spend 1-2 weeks on assessment and PoC before writing any Terraform finish faster than teams that start building on day one. Counter-intuitive, but consistently true across every migration I’ve delivered.
If you’re planning an AWS migration and want to avoid these patterns, I wrote a comprehensive guide covering the 7R strategies, AWS migration tools, real cost breakdowns and the full 6-phase process based on what actually works in practice.
I’m Jerzy, founder at Devopsity – we help companies migrate to AWS and build cloud-native infrastructure. If you’re stuck mid-migration or evaluating whether to start one, I’m happy to chat.
답글 남기기