Multi-Cloud · lesson 1 of 8
Intermediate
5Advanced
3Also part of Multi-Cloud
8- + CloudWatch Monitoring Basics from AWS
- + Azure Monitor Basics from Azure
- + Cloud Monitoring Basics from Google Cloud
- + AWS Cost and Well-Architected Basics from AWS
- + Azure Cost Management Basics from Azure
- + GCP Cost and Billing Basics from Google Cloud
- + Availability, Regions, and the Arithmetic of Nines from Cloud Interviews
- + Scaling, Queues, and Backpressure from Cloud Interviews
Choosing a Cloud: A Decision Framework With Real Numbers
A weighted scoring framework — team skills, egress cost, service maturity, lock-in risk — run against three real scenarios to a concrete recommendation.
By SkillByExample · Published
“It depends” is true and useless. What makes it useful is naming what it depends on, putting a number on each factor, and running the same four-factor scoresheet against your actual scenario instead of a generic one.
The four factors
Score each cloud 0-10 on each factor, weight the factors by what your scenario actually cares about, and take the weighted sum. The factors:
- Team skills — what your engineers already know. Ramp-up time is a real, recurring cost that shows up as slower delivery for months, not a one-time tax.
- Egress cost — the list-price rate per GB to move data out to the internet. It only matters in proportion to how much data you actually move out.
- Service maturity for your workload — not “which cloud has more services” in the abstract, but which one has the specific managed service your workload needs, and how battle-tested it is.
- Lock-in risk — how much of your architecture depends on a proprietary API versus a portable one (standard SQL, Kubernetes, S3-compatible object storage).
Scoring three real scenarios
# choose_cloud.py
def recommend(name, weights, scores):
clouds = ("AWS", "Azure", "GCP")
totals = {c: sum(w * scores[f][c] for f, w in weights.items()) for c in clouds}
ranked = sorted(totals.items(), key=lambda kv: -kv[1])
print(name)
for cloud, total in ranked:
print(f" {cloud:<6}{total:5.2f}")
print(f" -> recommend {ranked[0][0]}\n")
# Scenario 1: 5-engineer startup, greenfield SaaS, shipping speed matters most.
recommend(
"Startup, greenfield SaaS",
{"team_skills": 0.45, "service_maturity": 0.30, "egress_cost": 0.05, "lock_in_risk": 0.20},
{
"team_skills": {"AWS": 8, "Azure": 5, "GCP": 6}, # two engineers used AWS at a bootcamp
"service_maturity": {"AWS": 9, "Azure": 7, "GCP": 7},
"egress_cost": {"AWS": 7, "Azure": 7, "GCP": 5},
"lock_in_risk": {"AWS": 5, "Azure": 5, "GCP": 7},
},
)
# Scenario 2: 300-engineer enterprise migrating an on-prem Windows/AD estate.
recommend(
"Enterprise .NET migration",
{"team_skills": 0.40, "service_maturity": 0.30, "egress_cost": 0.05, "lock_in_risk": 0.25},
{
"team_skills": {"AWS": 4, "Azure": 9, "GCP": 3}, # existing AD, SCCM, on-prem Windows
"service_maturity": {"AWS": 8, "Azure": 9, "GCP": 5}, # Entra hybrid join is the deciding line
"egress_cost": {"AWS": 7, "Azure": 7, "GCP": 5},
"lock_in_risk": {"AWS": 6, "Azure": 5, "GCP": 6},
},
)
# Scenario 3: analytics platform moving ~40 TB/month out to partner systems.
recommend(
"Data-heavy analytics platform",
{"team_skills": 0.15, "service_maturity": 0.30, "egress_cost": 0.35, "lock_in_risk": 0.20},
{
"team_skills": {"AWS": 6, "Azure": 5, "GCP": 6},
"service_maturity": {"AWS": 8, "Azure": 6, "GCP": 9}, # BigQuery's serverless model fits this shape
"egress_cost": {"AWS": 6, "Azure": 6, "GCP": 4}, # GCP's list rate is the highest of the three
"lock_in_risk": {"AWS": 5, "Azure": 5, "GCP": 6},
},
)
$ python choose_cloud.py
Startup, greenfield SaaS
AWS 7.65
GCP 6.45
Azure 5.70
-> recommend AWS
Enterprise .NET migration
Azure 7.90
AWS 5.85
GCP 4.45
-> recommend Azure
Data-heavy analytics platform
AWS 6.40
GCP 6.20
Azure 5.65
-> recommend AWS
The first two results are unsurprising once you see the weights: a startup with no Windows estate weights team skills and service breadth over everything else, and lands on AWS’s larger catalog of battle-tested building blocks; an enterprise with an existing Active Directory forest weights team skills and identity-service maturity hard enough that Azure wins even though it scores lower on raw service count.
The third result is the one worth sitting with. GCP scores highest on service maturity for analytics — BigQuery’s serverless model genuinely fits this shape of workload better than Redshift or Synapse for many teams. But this scenario weights egress at 0.35 because the platform serves results out to partner systems on other clouds, and GCP’s list-price egress rate is the highest of the three. That single factor, at that weight, is enough to flip the recommendation to AWS. Change the scenario so results stay inside GCP — feeding Looker Studio or BigQuery ML instead of exporting to partners — and egress drops to near zero, the weight on it should drop too, and GCP wins outright. The framework isn’t rigged toward any cloud; it’s rigged toward whatever you tell it actually costs you money.
Why this beats “it depends”
Vendor documentation cannot honestly run this scoring, because a vendor can’t assign its own competitors a fair score, and won’t publish a scenario where it loses. That’s the gap this lesson fills: the same four factors, scored against your actual constraints, computed the same way every time.
Practice
1. Change scenario 3 so results stay entirely inside GCP (BigQuery to Looker Studio, no partner export) and re-run.
recommend(
"Data-heavy analytics platform (internal only)",
{"team_skills": 0.15, "service_maturity": 0.45, "egress_cost": 0.10, "lock_in_risk": 0.30},
{
"team_skills": {"AWS": 6, "Azure": 5, "GCP": 6},
"service_maturity": {"AWS": 8, "Azure": 6, "GCP": 9},
"egress_cost": {"AWS": 6, "Azure": 6, "GCP": 4},
"lock_in_risk": {"AWS": 5, "Azure": 5, "GCP": 6},
},
)
Data-heavy analytics platform (internal only)
GCP 6.95
AWS 6.75
Azure 5.65
-> recommend GCP
Dropping the egress weight from 0.35 to 0.10 and raising service maturity’s weight is enough to flip the winner to GCP — the same inputs, a different scenario, a different honest answer.
2. Add a fifth factor, "compliance certifications," and give it real weight in the enterprise scenario.
weights = {"team_skills": 0.30, "service_maturity": 0.25, "egress_cost": 0.05,
"lock_in_risk": 0.20, "compliance": 0.20}
scores["compliance"] = {"AWS": 8, "Azure": 9, "GCP": 7}
Re-running with these weights keeps Azure on top (compliance reinforces the existing lean here), but the margin changes — which is the point of scoring instead of guessing: you can see how sensitive the recommendation is to a factor you just added.
3. Set every weight equal (0.25 each) for all three scenarios and compare to the weighted results above.
Equal weights erase the scenario-specific reasoning entirely — a startup and an enterprise get scored identically, which is exactly the “it depends” answer this framework exists to avoid. The lesson: the weights, not the raw scores, are where your actual judgment lives.
4. Score your own current project against these four factors.
Write down honest 0-10 scores for the cloud(s) you’re evaluating and weights that reflect what actually matters for the project — not what you’d like to matter. If the result disagrees with your gut, that disagreement is worth investigating before you commit.
Next: the same “grant read-only access to one bucket” task, done three ways, to see where the IAM vocabulary differs and where the underlying model doesn’t.