Engineering Atlas › System Design Interviews

AWS Lambda for system design interviews

13 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

Invocation models

Synchronous invocations (API Gateway, function URLs, SDK Invoke with RequestResponse) wait for the result and return errors to the caller. Asynchronous invocations (S3, SNS, EventBridge, Event type) are queued internally; Lambda retries twice on error, one and then two minutes apart, and can send failures to a destination or DLQ (AWS docs). Event source mappings are Lambda-managed pollers that read SQS, Kinesis, DynamoDB Streams or Kafka in batches and invoke your function synchronously, with retry and checkpoint behaviour defined by the source.

The execution environment lifecycle

An environment goes through Init (start extensions, start the runtime, run code outside the handler), then repeated Invoke phases, then Shutdown when Lambda reclaims it. Between invocations the environment is frozen: background threads stop and timers do not fire. Anything you create during init (SDK clients, database connections, caches in globals, files in /tmp) survives into later invocations on the same environment, which is the main warm-start optimisation.

Concurrency

If a function handles 200 requests per second at 50 ms each, it needs about 200 x 0.05 = 10 concurrent environments. Reserved concurrency caps a function and guarantees it that share of the account quota. Provisioned concurrency keeps a number of environments initialised in advance, removing cold starts for that many concurrent requests at an hourly price. SnapStart (Java, Python, .NET) snapshots an initialised environment and restores new ones from the snapshot instead of running init.

One request per environment is the classic model, now one of several. Lambda Managed Instances (announced December 2025) run functions on EC2 instances in your account, isolated by containers on Nitro rather than Firecracker, and let one environment serve many invocations at once; they scale on CPU without cold starts and cost the EC2 price plus a 15% management fee (AWS docs). Durable functions checkpoint each step and replay after interruptions, so one workflow can run for up to a year and wait without paying for compute (AWS docs). AWS now calls the classic kind Event Functions, next to Web Functions, which run an ordinary HTTP server with 64 concurrent requests per environment by default (Lambda quotas).

Down the stack

Layer 1The contract

Lambda promises at-least-once execution for asynchronous and event-source invocations: retries and internal redelivery mean your handler can run more than once for the same event, so handlers must be idempotent. A synchronous invoke runs at most once per request from Lambda's side, but client retries can repeat it. Each environment handles one request at a time, so per-environment code needs no locking. Functions scale until they hit the account or reserved concurrency limit, after which synchronous calls get 429 TooManyRequestsException and asynchronous events are retried for up to 6 hours. Scaling speed is bounded too: each function can add 1,000 concurrent environments every 10 seconds, refilled continuously and never banked (AWS docs). Warm invocation overhead is typically a few milliseconds; cold starts add roughly 100 ms to over 1 s depending on runtime, package size and init work.

Layer 2The mechanism

Routing. A front-end fleet authenticates the request and asks an assignment service for an idle environment of that function version. If one exists, the request goes to its worker; if not, a placement service picks a worker with capacity and creates one. AWS has described rebuilding assignment around a replicated log so environment state survives failures (hedge: from re:Invent talks, not formal docs).

Workers and Firecracker. Workers are bare-metal EC2 hosts running many Firecracker microVMs. Firecracker is a Rust virtual machine monitor on KVM with a minimal device model (virtio net and block, serial console), booting a guest kernel in under 125 ms with under 5 MiB of overhead per microVM, and up to 150 microVMs per second per host (Firecracker). Each microVM holds a guest kernel, the runtime and your code for one function in one account, giving VM-level isolation between tenants.

Code delivery. Zip packages (50 MB zipped, 250 MB unzipped) and container images (10 GB) are cached near workers. Image layers are split into chunks, deduplicated across customers with convergent encryption, and loaded lazily, so a 1 GB image need not download fully before start (Brooker et al., USENIX ATC 2023).

Runtime API. The runtime loops on GET /2018-06-01/runtime/invocation/next, calls your handler, then POSTs to /runtime/invocation/<request-id>/response (or /error). Custom runtimes plug in here. For SQS, Lambda-managed pollers long-poll, batch, invoke and delete on success, scaling with backlog up to the mapping's maximum concurrency.

Layer 3The machine

Memory is the single sizing knob: CPU is allocated in proportion, reaching one full vCPU at 1,769 MB and up to 6 vCPUs at 10,240 MB. A CPU-bound function at 512 MB gets about 0.3 of a vCPU, so doubling memory can halve duration at the same cost. Billing is per millisecond of duration times configured memory, $0.0000166667 per GB-second on x86 and $0.0000133334 on Arm (20 percent less) in the cheapest tier, plus $0.20 per million requests (AWS pricing).

A cold start splits into sandbox creation (tens of ms, with pre-created pools), code loading (proportional to bytes touched), runtime start (a few ms for Node or Python, hundreds for a JVM) and your init code, often the largest part. Init is capped at 10 seconds for on-demand functions (AWS docs). Since 1 August 2025 that init time is billed for every configuration; before, on-demand zip functions on managed runtimes got it free (AWS Compute Blog). SnapStart instead restores memory pages from a snapshot on demand, so code assuming uniqueness at init (random seeds, connections) needs care.

Idle environments are reclaimed after an unspecified period, commonly observed as minutes. VPC-attached functions use shared Hyperplane ENIs, so VPC attachment no longer adds seconds to cold starts as it did before 2019 (AWS Compute Blog).

Why it's built this way

  1. Customers wanted to pay only for actual work, so capacity is allocated per request rather than per server, which requires creating and destroying sandboxes quickly.
  2. Running untrusted code from many accounts on shared hosts requires VM-grade isolation, but traditional VMs boot in seconds and cost hundreds of MB, so AWS built Firecracker to make VMs as cheap as containers.
  3. One request per environment keeps the programming model simple and isolation clean, at the cost of more environments for concurrent load.
  4. Cold starts are the price of not reserving capacity, so Lambda reuses warm environments, and offers provisioned concurrency and SnapStart for those who will pay to avoid them.
  5. Fleet stability needs limits, so concurrency quotas, scaling rates and timeouts protect both AWS and downstream systems from runaway fan-out.

Diagram

sequenceDiagram
    participant C as Caller
    participant F as Front end
    participant A as Assignment service
    participant P as Placement
    participant W as Worker (Firecracker microVM)
    C->>F: Invoke
    F->>A: need env for fn:v3
    alt warm env idle
        A-->>F: env 17 on worker W
    else none idle
        A->>P: create env
        P->>W: boot microVM, load code, run init
        W-->>A: env ready (cold start)
        A-->>F: new env on W
    end
    F->>W: event via runtime API (/invocation/next)
    W-->>F: POST /response
    F-->>C: result

Worked example

An image-thumbnail service receives S3 upload events: 2 million images a day, with a launch-day spike of 3,000 uploads per second for 10 minutes. Each thumbnail takes 400 ms at 1,024 MB.

Normal load averages about 23 per second, needing about 23 x 0.4 = 9 concurrent environments. The spike needs 3,000 x 0.4 = 1,200, above the default regional quota of 1,000, and that quota is shared with every other function in the account. The team requests a quota increase to 3,000 and sets reserved concurrency of 1,500 on the thumbnail function so it cannot starve the customer-facing API. Because S3 events invoke asynchronously, any throttled events are retried by Lambda for up to 6 hours, so the spike causes delay rather than loss.

Cost: 2 million x 0.4 s x 1 GB = 800,000 GB-seconds a day, about $13. Containers sized for the peak would sit mostly idle, so Lambda wins. At a steady 3,000 per second all day the same math gives about $1,700 a day, where containers would cost a fraction.

Python with Pillow cold-starts in about 300 ms; about 1,200 environments start cold once during the spike, then are reused. Nobody waits synchronously on thumbnails, so no provisioned concurrency is needed.

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
LambdaNo servers, per-ms billing, scales from zeroCold starts, 15-minute limit, cost at steady high loadSpiky, event-driven work and glue
Lambda + provisioned concurrencyPredictable latencyPays for idle capacityLatency-sensitive APIs with known baseline
ECS Fargate or Cloud RunContainers, long-running, no host managementSlower scaling (tens of seconds), per-task overheadSteady services, long jobs
EC2 or EKSCheapest at steady load, full controlYou run capacity and patchingLarge steady workloads, special hardware
Step Functions + LambdaOrchestration beyond 15 minutes with retriesMore moving parts, per-transition costMulti-step workflows across many services
Lambda durable functionsWorkflows in plain code for up to a year, free while waitingCode outside steps re-runs on every replay; newerMulti-step logic that belongs inside one function
Lambda Managed InstancesMany requests per environment, EC2 pricing and hardware, no cold startsAlways-on minimum instances, 15% fee, handlers must be thread-safeHigh steady volume that should keep Lambda's event integrations

Pitfalls in production

Leadership lens

Check yourself

  1. Why does increasing a function's memory sometimes reduce its cost?

    Show answer

    CPU scales with memory, so a CPU-bound function finishes proportionally faster and GB-seconds stay flat or fall while latency improves.

  2. A function serves 500 requests per second at 80 ms each. How many concurrent environments does it need, and what happens at the default quota?

    Show answer

    About 40, well within a 1,000 quota; at 100 times that load it would need 4,000 and be throttled with 429 errors.

  3. Why can global variables be both useful and dangerous in a Lambda handler?

    Show answer

    They persist on a warm environment, enabling connection and cache reuse, but can leak per-request data between users and are not shared across environments.

  4. Why must an SQS-triggered function be idempotent even if it never fails?

    Show answer

    SQS and the poller provide at-least-once delivery, so a message can be delivered twice, for example when a visibility timeout expires during a slow batch.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →