InterviewKata
System design · Worked design D42 of 46 · Data and infrastructure · Long form

Design a serverless functions platform

Design a platform like AWS Lambda or Google Cloud Run functions: developers upload a function, and it runs whenever an HTTP request or an event arrives, from zero copies to thousands in seconds, billed by the millisecond. The platform runs code from strangers side by side on shared machines, so it must start sandboxes fast, keep tenants strictly apart, and pack them tightly enough to make money.

1. The prompt

"Design a serverless compute platform: customers deploy functions that run on requests or events, scale automatically, and pay only while their code runs."

What makes it hard:

2. Frame the problem

How much traffic?

1 million deployed functions, 2 million invocations a second at peak. Median run time 100 ms; maximum 15 minutes.

By Little's law, 2 M/s × 0.1 s ≈ 200,000 sandboxes busy at any moment, plus idle warm ones.

What can a function be?

Code for a supported runtime (Node.js, Python, Java, Go…), or a container image, with 128 MB to 10 GB of memory.

CPU is handed out in proportion to memory, so memory is the unit of packing.

How is it triggered?

Synchronously (HTTP, an API call that waits for the result) and asynchronously (events, queues, schedules, where the caller doesn't wait).

Async needs a durable queue, retries and a dead-letter destination.

Concurrency model?

One request at a time per sandbox. More requests at once means more sandboxes.

Simple for customers to reason about; it makes sandbox creation speed the core problem.

Isolation?

Assume hostile tenants. Separate accounts must never share a sandbox or a kernel.

That rules out plain containers as the only boundary.

3. What it must do

  1. Deploy a function version (code, runtime, memory, timeout, environment).
  2. Invoke it synchronously or from events, running each invocation in an isolated sandbox.
  3. Scale automatically from zero to the account's limit, and back to zero.
  4. Meter and report: logs, metrics, and billing per millisecond of run time.

Not now: long-running services (that's a container platform), GPUs, a function marketplace, cross-region failover of individual invocations.

4. How well it must do it

TargetWhy this number
Warm invocation overhead under 10 ms at p99 speedMost requests should feel like calling an ordinary service.
Cold start under 500 ms at p99 for small functions, platform part only speedThe customer's own initialisation (loading a framework) comes on top; we can only control our part.
Isolation: no data or resource leaks between tenants securityOne breach ends the business.
Async events: never lost; retried, then sent to a dead-letter destination data safetyEvents are often the only record of something that happened.
Scale-up: +1,000 concurrent sandboxes for one function within about 10 s elasticityTraffic spikes are why people choose serverless.

5. Numbers that change the design

QuantityWorking→ so we…
Busy sandboxes2 M/s × 0.1 s ≈ 200,000, average size 512 MB ≈ 100 TB of memory busy; with warm idle sandboxes roughly double→ so ~1,000 hosts with 256 GB each, packed by memory
Sandbox overheadA container on a shared kernel costs little but isolates weakly; a full VM isolates well but takes seconds and hundreds of MB→ so lightweight microVMs: Firecracker reports under 125 ms boot and under 5 MiB of overhead per VM
Code to fetch on a cold startA 50 MB package at ~1 GB/s from object storage ≈ 50 ms; a 2 GB container image ≈ 2 s→ so code is cached on hosts, and large images are loaded lazily in chunks
Stored code1 M functions × several versions × ~50 MB ≈ 100+ TB→ so packages live in object storage, content-addressed (shared layers stored once)

Illustrative numbers. Firecracker figures are from its project page (checked October 2026).

6. The nouns

function_version  (account, name, version) → runtime, memory_mb, timeout_s, code_digest, env (encrypted)
sandbox           id → host, function_version, state (creating | idle | busy), idle_since       (in the worker manager's memory)
host              id → zone, free_memory, cached code digests, healthy
invocation        id → function_version, sandbox, start, duration_ms, status       (stream → logs, billing)
event_queue       per async function: durable queue, attempts, dead-letter destination

7. The contract

PUT  /v1/functions/{name}              {runtime, memory_mb, timeout_s, code (zip or image digest)} → version
POST /v1/functions/{name}/aliases/prod  {version}
POST /v1/functions/{name}:invoke        body → result     (sync; 429 when over the concurrency limit)
POST /v1/functions/{name}:invoke-async  body → 202        (queued; retried twice, then dead-letter)
Triggers: HTTP routes, queue and stream subscriptions, schedules
Inside the sandbox: the runtime fetches the next event from a local runtime API, runs the handler, posts the result

8. First working version

A front end authenticates calls and enforces limits. A worker manager knows, for each function version, which sandboxes exist and which are idle. A placement service chooses a host for a new sandbox. On each host, an agent creates microVMs and caches code.

8.1 Warm and cold invocations

8.2 An event-triggered function

Parked for latercold starts (9.1) · isolation (9.2) · packing hosts (9.3) · limits and bursts (9.4) · the database behind the functions (9.5).

9. Make it hold up

9.1 Making cold starts rare and short speed · cost

ApproachWhy
✗ BadCreate a fresh sandbox for every invocationEvery request pays boot + code fetch + runtime start: hundreds of ms to seconds.
~ GoodKeep every sandbox warm foreverNo cold starts after the first, but memory is held for functions that ran once last week. The platform can't afford it, so customers would pay for idle time.
✓ GreatReuse idle sandboxes for a few minutes, and make creating one cheap: microVMs that boot fast, code cached on hosts (and lazily loaded image chunks), snapshots of an initialised runtime restored instead of booted, and paid provisioned concurrency for functions that can't tolerate any cold startBusy functions almost never see a cold start; rarely used ones pay a short one; customers who need a guarantee pay for warm capacity explicitly.

The customer controls the rest: a small package, few dependencies, and work done lazily rather than at start-up. The lab in section 13 shows how the keep-alive time trades cold starts against idle memory.

9.2 Strangers on one machine security

9.3 Packing hosts cost · reliability

9.4 Limits and bursts fairness

Each account has a concurrency limit (say 1,000 sandboxes) and a burst rate (how fast new sandboxes may be added). Limits protect the platform from one customer's runaway loop, and protect the customer from a surprise bill. A function can have reserved concurrency: guaranteed capacity, and also a cap, which is how a customer stops a function from overwhelming their database. Over the limit, synchronous calls get 429; asynchronous events wait in their queue and are retried.

9.5 The database behind the functions reliability

A traffic spike scales the function to 1,000 sandboxes, each opening its own database connection: the platform scaled perfectly and the customer's database fell over. Recommend a connection proxy or pooler in front of the database, reserved concurrency as a cap, and short-lived connections or an HTTP data API. Functions are stateless; anything that must survive goes to a database, object storage or a queue, never to the sandbox's disk.

9.6 The whole design after the deep dives

10. Interviewer follow-ups

A host dies with 300 busy sandboxes.

Synchronous callers get an error and may retry; async invocations are still in their queue (deleted only on success), so they run again elsewhere. The worker manager drops the host's sandboxes and new ones are created on other hosts.

The worker manager is a single point of failure.

Partition it by function (consistent hashing), with replicas per partition. Its state (which sandboxes exist) can be rebuilt from host agents' reports, so losing a partition costs a burst of cold starts, not data.

A function runs for its full 15 minutes on every call because of a bug.

The timeout kills it and it's billed for 15 minutes; concurrency limits stop it multiplying. Alarms on duration and error rate tell the customer.

How do you deploy a new version of the platform's own host software?

Drain hosts in small waves: stop placing new sandboxes, let busy ones finish (up to the maximum timeout), then upgrade. Never upgrade a whole zone at once (Module 27).

How is billing computed?

The host agent records start and end of each invocation; duration × memory size (GB-seconds) plus a per-request fee, aggregated from the invocation stream, as in D17.

Snapshots restore the same memory into many sandboxes. Any danger?

Yes: anything random generated during initialisation (seeds, unique IDs, keys) is now identical in every copy. Runtimes must re-seed after restore, and the platform should offer a hook for it.

11. When things break

FailureWhat users seeHow we detect itWhat limits the damage
Host lostSome in-flight calls failAgent heartbeatsFunctions spread over hosts and zones; async retried
Code store slowLong cold startsFetch latencyHost code caches; warm sandboxes unaffected
Placement service downNo new sandboxes; warm ones keep workingPlacement errorsReplicated; worker manager queues briefly
One account burstsThat account throttledConcurrency per accountAccount limits and burst rates
Sandbox escape attemptNothing, if containedHost security monitoringMicroVM boundary, minimal host attack surface, no cross-tenant reuse
Event queue backlogAsync work delayedQueue agePollers scale out; events kept until done

12. Wrap-up and the clock

Trade-offs we made:

What we'd watch: cold start rate and duration (platform part) · warm overhead · host memory utilisation · sandbox creation rate per host · throttles per account · async queue age and dead-letter counts.

At 10×: more hosts and worker manager partitions. The cost lever is utilisation: better snapshots and faster creation let idle sandboxes be reclaimed sooner.

A 60-minute interview

0–5Frame: traffic, durations, triggers, isolation
5–10Must do, how well, Little's law
10–15Nouns and contract
15–25First version: warm and cold paths
25–50Deep dives: cold starts (9.1), isolation (9.2), limits (9.4)
50–60Packing or async, follow-ups, wrap-up

A 90-minute interview

0–8Frame, including the concurrency model
8–15Must do, how well, numbers
15–22Nouns, contract
22–35First working version, sync and async
35–75Deep dives 9.1–9.5 and the final picture
75–85Follow-ups: host loss, snapshots, billing
85–90Trade-offs, metrics, 10×
LevelWhat a strong answer shows
Mid-levelContainers started on demand, a router that reuses running ones, autoscaling, a queue for events.
SeniorWarm and cold paths, Little's law for capacity, microVM isolation, code caching and snapshots, limits and reserved concurrency, async retries and dead letters.
Staff+Packing economics and keep-alive as a cost trade-off, snapshot uniqueness pitfalls, failure domains for the control plane, protecting customers' downstream systems.

Common mistakes:

13. Try it, check yourself, read more

The keep-alive lab. A function gets requests at random. Choose how often, how long each runs and how long the platform keeps an idle sandbox, and see the cold start rate and the memory held idle.

Requests per minute
Run time per request
Keep idle sandboxes for

A simple model: requests arrive at random (Poisson); a request is cold if no sandbox has been used within the keep-alive time. At high rates, cold starts come from scaling up, which this model ignores. 512 MB sandboxes.

A function gets 20,000 requests a second and each takes 100 ms. Roughly how many sandboxes are busy at once?

Little's law: concurrency = rate × duration = 20,000 × 0.1.

Which isolation fits untrusted code from many customers on one host?

A hardware-virtualised boundary with a tiny attack surface, at container-like speed.

What cuts the runtime part of a cold start the most for a Java function?

The JVM and framework start once; later sandboxes resume from memory.

A spike scales a function to 1,000 copies and the customer's database runs out of connections. The fix?

Elastic compute needs a brake in front of inelastic dependencies.
ResourceWhy
Firecracker: lightweight virtualization for serverless applications (NSDI 2020)Why AWS built microVMs for Lambda, and what they cost.
Firecracker project pageBoot time, overhead and the security model.
Lambda provisioned concurrencyA product's version of paid warm capacity.
gVisorA different isolation approach: a user-space kernel in front of the host.