all_lessons / system_design / cases / C43 C43 / C44

IaaS, PaaS, SaaS, and cloud choice

Every cloud decision is the same decision asked at a different layer: which slices of the stack do you operate yourself, and which do you pay someone to operate for you? The bill and the blame move together.

Source: Archive Case drill Trade-off first
The hinge — this is a responsibility trade, not a technology choice
IaaS / PaaS / SaaS are not three products you pick between like databases. They are three points on one continuous axis: how much of the stack you operate. The whole case turns on a single first principle — you cannot buy away responsibility without also buying away control, and you cannot keep control without also keeping the operational headcount that control requires. Every concrete decision (managed DB vs self-run, portable abstraction vs native service, single vs multi-cloud) is that one trade re-asked at a finer grain. So derive the answer by counting the layers you operate and pricing what each costs to run.

The stack ladder: who owns each layer

Draw the stack as a ladder from bare metal up to the running business workflow, and mark — for each model — where the boundary between "provider operates this" and "you operate this" falls. The number of rungs below the line is your operational surface area; the number above it is what you've handed off (and lost direct control over).

RESPONSIBILITY LADDER (▣ = you operate · □ = provider operates) layer On-prem IaaS PaaS SaaS ─────────────────────────────────────────────────── business workflow ▣ ▣ ▣ ▣ ← always yours: your domain application code ▣ ▣ ▣ □ runtime / libs ▣ ▣ □ □ middleware / scaling ▣ ▣ □ □ OS / patching ▣ ▣ □ □ virtualization ▣ □ □ □ servers / hardware ▣ □ □ □ storage ▣ □ □ □ networking ▣ □ □ □ ─────────────────────────────────────────────────── layers YOU operate: 9 4 1 0(+config) the line you draw IS the decision. everything below it is undifferentiated heavy lifting you've chosen to keep.

That bottom row is the load-bearing count. On-prem you operate all nine rungs. IaaS hands off the four hardware/virtualization rungs and leaves you the four that actually differentiate nobody — OS, middleware, runtime, app — plus your workflow. PaaS pushes the line up to "app only": you ship code, the platform runs everything beneath it. SaaS pushes it to the top: you operate nothing but configuration of someone else's finished workflow. Read it as a dial, not three boxes: each rung you hand up the ladder removes an operational duty and a degree of control in the same motion.

ModelLayers you operateYou still ownCanonical exampleYou give up
On-prem9 (everything)Hardware → workflowYour own datacenterNothing — and that's the problem (capex, capacity planning, all ops)
IaaS4 — OS, middleware, runtime, appOS patching, scaling logic, codeEC2, GCE, bare VMs + K8sHardware control; gain elastic capacity
PaaS1 — app code onlyCode + data + configApp Engine, Heroku, Cloud Run, RDSRuntime/OS/scaling control; gain deploy speed
SaaS0 — config onlyConfig + your data inside itWorkday, Stripe, Datadog, GmailAlmost all control; gain "it just works"

This is DDIA Ch. 1's framing made concrete: reliability, maintainability, and operability are properties someone must provide, and the cloud model just decides who. Buying a managed service is buying operability — the question is always whether you can buy it cheaper than you can build and run it.

Put numbers on it — the cost crossover

The single calculation that decides most of these debates is the managed-vs-self crossover: a managed service charges a premium per unit (typically ~2–3× the raw compute/storage of running it yourself), and self-running charges you in SRE headcount. The break-even is where the premium equals the salary you'd otherwise pay to operate it well.

Worked example — managed Postgres vs self-run on VMs
Say the raw infrastructure for your database tier (VMs, disks, replicas) is $4,000/mo self-run. A managed equivalent at a 2.5× premium is 4000 · 2.5 = $10,000/mo → a premium of $6,000/mo = $72,000/yr. The rule that falls out: managed is cheap when you're small (headcount dominates) and expensive when you're huge (premium dominates). The crossover for a database tier sits somewhere around the point where the annual premium exceeds ~1.5 fully-loaded SRE salaries.
Managed DB premium (typical)
2–3×
Cost to self-run a DB well
~0.5–1.5 SRE
Egress, cross-cloud (per GB)
~$0.08–0.12
Egress crossover, 100 TB/mo
~$10k/mo

Egress is the multi-cloud killer and deserves its own number. Intra-cloud, same-region traffic is usually free; cross-cloud egress runs ~$0.08–0.12/GB. If a multi-cloud design shuttles 100 TB/mo between providers, that's 100,000 GB · $0.10 = $10,000/mo = $120,000/yr of pure tax for the privilege of spanning two clouds — before you've paid for the duplicated tooling, the doubled on-call surface, or the engineers who now must be fluent in two of everything. Egress is the gravity well that quietly makes "portable" architectures expensive, and it is why data, once it lands in a cloud, tends to stay there.

Clarify the contract

Treat the prompt as a decision contract, not a component diagram. What this case must do:

And, equally important, what it need not do: it need not produce a portable, cloud-agnostic design by default (portability is a cost, not a virtue — see the deep dive); it need not be multi-cloud (almost never worth it); and it need not minimize the per-unit bill (minimizing total cost, including headcount and risk, is the real objective).

Data model & surface

The artifacts that actually drive this decision are not request schemas — they're the operational records that let you reason about ownership and cost.

ArtifactWhy it exists
service catalogMaps each stack layer to a chosen operating model — the ladder, written down.
cost line items (tagged)Compute / storage / IO / egress / support, per service, so the crossover is measurable not guessed.
SLA + runbookFor anything self-run: who gets paged, and the failover/backup procedure that headcount enforces.
data residency policyEncodes which regions/providers data may live in — the real driver behind multi-region/-cloud.

Linearized design — deciding the line, layer by layer

Walk the decision the way you'd actually make it, top-down, and the first real bottleneck (stateful services + egress) surfaces naturally.

  1. 1. List the rungs. Hardware → network → OS → middleware → runtime → app → workflow. This is the ladder diagram above; the workflow rung is always yours.
  2. 2. Default the undifferentiated rungs to managed. Nobody differentiates by patching an OS or babysitting a load balancer. Hand those up the ladder unless a constraint forbids it. This is the same "buy operability" move as DDIA Ch. 5–6's managed replication and partitioning: a managed DB is bought-vs-built leader/follower replication and resharding.
  3. 3. Identify the rungs that differentiate you. Your application code and domain workflow — keep those. If a low-level rung genuinely encodes your competitive edge (a hand-tuned storage engine at hyperscale), keep that one too, deliberately.
  4. 4. Price the stateful tier both ways. The database is where the managed-vs-self crossover bites hardest. Run the premium-vs-headcount math above for your scale.
  5. 5. Model egress explicitly. Cross-region and especially cross-cloud traffic; this is the line item that silently dominates and the one that kills multi-cloud.
  6. 6. Document lock-in at three levels — API, data, operational — and design exit paths only where business risk justifies the recurring portability tax.

Deep dives

1. Lock-in vs native-service leverage

The reflexive fear is vendor lock-in, so teams reach for a "portable abstraction" — wrap everything behind interfaces, use only services with an open-source equivalent, avoid anything proprietary. The cost of that posture is rarely named: a portable abstraction can only expose the least-common-denominator of every backend it abstracts. If you insist your queue work on SQS and Pub/Sub and Kafka, you get the intersection of their features — not the union. You forgo SQS's native dead-letter ergonomics, Pub/Sub's exactly-once, the managed scaling — exactly the leverage you were paying the cloud for in the first place.

This is the same abstraction-leverage trade DDIA draws around storage engines: a generic interface buys you swappability and costs you the specialized behavior the underlying engine could have given you. Portability is a real, recurring tax (the engineering to maintain the abstraction, the features foregone, the testing matrix) paid every day against a migration that usually never comes.

When lock-in is actually fine — most of the time. Lock-in is bad only when it traps a critical system in a cost, compliance, or capability corner with no exit. For most components it's a non-issue: the switching cost is real but bounded, and the native-service leverage you gain every single day vastly outweighs a migration you'll likely never run. The mature move is deliberate, scoped lock-in: lean hard into native services for the 90% that's undifferentiated, and reserve portability investment for the narrow slice (often the primary datastore, or a regulated workload) where being trapped would genuinely be fatal.

2. Managed vs self-run — the total-cost picture

The crossover math above is the skeleton; here's the flesh. The per-unit premium (2–3×) is the visible cost of managed. The invisible cost of self-run is everything that makes a database production-grade and never appears on the AWS bill: the on-call rotation (you can't run a 24/7 rotation with fewer than ~4–5 people without burning them out), the backup/restore drills, the version-upgrade projects, the failover testing, and the single-point-of-knowledge risk when your one DB expert leaves.

So the honest comparison is not "managed premium vs. raw infra savings" — it's "managed premium vs. (headcount + incident risk + opportunity cost of those engineers not building product)." For a small or mid team the managed side wins decisively because headcount is lumpy and expensive and you can't hire half an SRE. The self-run side only wins at a scale where the premium dwarfs a full, well-staffed ops team — and at that scale you're also the kind of org that can run it well. Below that line, paying the premium is buying back engineering focus, which is almost always the scarcer resource.

3. When multi-cloud is worth it (usually not)

Multi-cloud is the most over-prescribed pattern in this space, usually justified by "avoiding provider lock-in" or "surviving a provider outage." Both arguments mostly don't survive the arithmetic.

The availability math rarely justifies the tax
A single hyperscaler region delivers ~99.9–99.99% on a well-architected multi-AZ deployment. Going multi-region within one cloud buys you most of the remaining nines for a fraction of multi-cloud's complexity. Going truly multi-cloud (active/active across providers) might lift you from, say, 99.99% to 99.995% — a few minutes per year — while imposing the full operational tax: two control planes, doubled IaC, engineers fluent in two of everything, least-common-denominator services (deep dive 1), and the ~$120k/yr egress bill from the numbers above for moving 100 TB/mo between clouds. You pay a permanent, large tax to hedge an outage that a multi-region single-cloud design already mostly covers.

The one place multi-cloud genuinely earns its keep is regulatory data residency: when law requires data to live in a jurisdiction or under a provider that your primary cloud can't serve, multi-cloud (or at least multi-provider for that slice) stops being a hedge and becomes a hard requirement. That — not availability, not lock-in fear — is the real driver. Default to single-cloud, multi-region; reach for multi-cloud only when compliance forces your hand.

Trade-offs

ChoiceBuysCostsChoose when
Managed vs self-managedoperability (backups, failover, on-call) bought as a unit2–3× per-unit premium, less low-level controlsmall/mid teams, or any time the premium < ~1.5 SRE salaries
Single cloud vs multi-cloudsimplicity, volume discounts, free intra-cloud trafficprovider dependency; outage blast radiusnearly all products — use multi-region instead
PaaS vs Kubernetes/IaaSdeveloper velocity, one rung to operateplatform constraints, less tuning headroomstandard web/CRUD apps without exotic infra needs
Portable abstraction vs native servicea migration option held in reserveleast-common-denominator features + ongoing taxonly the critical-data slice, or a regulated workload

The sharpest of these is the multi-cloud row, and it's worth narrating because it's the one juniors get backwards. The instinct says "multi-cloud = resilient." The senior read is the opposite: multi-cloud adds failure surface (now two control planes can break you) and a permanent operational + egress tax, in exchange for an availability gain that a multi-region single-cloud design already captures more cheaply. Reach for it for residency law, not for nines.

Failure modes

FailureMitigation
Unexpected bill (egress / scale-out surprise)Tag every resource; alert on cost anomalies; model egress before shipping cross-region designs.
Managed service limit / quota hitKnow the quotas, design load tests around them, request increases ahead of launch.
Region / residency compliance violationEncode data residency in provisioning policy; fail provisioning that would place data out of jurisdiction.
Provider outageMulti-region within one cloud first (lesson 13's availability math); reserve multi-cloud for genuine residency needs.
Lock-in trap on a critical systemIdentify the one or two systems where being trapped is fatal; invest portability there only, accept lock-in elsewhere.

Interview prompts you should be ready for

  1. When is multi-cloud worth the complexity? (senior answer) Almost never for availability — a single hyperscaler multi-region design already gets you to ~99.99%, and true multi-cloud might add a few minutes a year while imposing two control planes, doubled IaC, least-common-denominator services, and a six-figure annual egress tax (100 TB/mo cross-cloud ≈ $120k/yr). The real driver is regulatory data residency: when law mandates a jurisdiction or provider your primary cloud can't serve. Default single-cloud, multi-region; go multi-cloud only when compliance forces it.
  2. How do you decide managed vs self-managed for a database? (senior answer) Price both: managed runs a ~2–3× per-unit premium; self-run costs ~0.5–1.5 fully-loaded SREs to do well (backups, PITR, failover drills, upgrades, 24/7 on-call). Crossover is where the annual premium exceeds ~1.5 SRE salaries. Below it, managed wins because you're buying back engineering focus; far above it, self-run wins on premium. Most teams are below the line.
  3. What's the cost of a portable, cloud-agnostic architecture? (senior answer) A portable abstraction can only expose the least-common-denominator of every backend it wraps, so you forgo the native-service leverage you were paying the cloud for. Plus a permanent engineering tax to maintain the abstraction and its test matrix — paid daily against a migration that usually never happens. Portability is a cost, not a default virtue.
  4. So is lock-in always bad? (senior answer) No. Lock-in is only bad when it traps a critical system in a cost/compliance/capability corner with no exit. For the 90% that's undifferentiated, lean into native services — the daily leverage beats a migration you'll likely never run. Make lock-in a deliberate, scoped decision: portability investment goes only to the slice where being trapped would be fatal.
  5. Map IaaS/PaaS/SaaS to who operates what. (senior answer) One axis, four cut points. On-prem: you run all nine rungs. IaaS: provider runs hardware/virtualization, you run OS+middleware+runtime+app (four rungs). PaaS: you run app code only. SaaS: you operate nothing but config. Each rung handed up removes an operational duty and a degree of control together — that's the whole trade.
  6. Why does egress dominate cloud cost models? (senior answer) Intra-region traffic is typically free, but cross-region and especially cross-cloud egress runs ~$0.08–0.12/GB, so 100 TB/mo between clouds is ~$120k/yr of pure tax. It's the gravity well that makes "portable" and multi-cloud designs quietly expensive and the reason data, once landed, tends to stay in one cloud.
  7. A small startup asks whether to run their own Kubernetes. (senior answer) Usually no. At small scale the operational rungs (cluster upgrades, node pools, networking, on-call) dominate, and a PaaS (Cloud Run, App Engine, a managed container platform) gets them to "app code only" — one rung — so the team builds product instead of operating infra. Adopt raw K8s when the team is large enough that the control it buys (custom scheduling, multi-tenancy, cost at scale) actually pays for the rungs you take back.

Related lessons

This case is the operating-model sibling of C42 (microservices / containers / serverless) — C42 decides how you decompose the app; C43 decides who operates each layer it runs on, and serverless is just PaaS pushed one rung further. The layer-by-layer "what do we own" reasoning is foundation lesson 17 (architecture decomposition) applied to the ops/vendor axis. The multi-cloud-vs-multi-region availability arithmetic leans directly on lesson 13 (fault tolerance) — you can't argue the nines without it. And 16 (observability) is where the cost tags and quota alerts that keep this decision honest actually live; 01 (method & napkin math) is the crossover arithmetic itself.

C42 · microservices/serverless Architecture decomposition Fault tolerance Observability Method and napkin math
Takeaway
Cloud choice is one decision asked at every layer: draw the ladder from hardware to workflow and decide where the line between "you operate" and "they operate" falls. The bill and the blame move together — handing a rung up the ladder buys operability and costs control. Default the undifferentiated rungs to managed (the ~2–3× premium beats ~1.5 SRE salaries until you're huge), lean into native services everywhere lock-in isn't fatal, and price egress before it prices you. Stay single-cloud and multi-region; reach for multi-cloud only when data-residency law — not outage fear — forces your hand.