AWS Lambda

What it is, common event sources, the execution environment and cold start model, reserved concurrency, and the anti-patterns and guarantees to design around.

August 29, 20265 min read9 / 15

The core compute service: a function that runs code in response to an event, with no server to manage. Serverless here means two specific things: pay only for actual execution time, and AWS runs the underlying servers, not you.

Lambda functions don't run continuously. They respond to an event and then go away, there's no long-running process sitting idle between invocations.

Common event sources

Any of these can trigger a Lambda function:

  • HTTP requests, usually through API Gateway. GET, POST, PUT, DELETE.
  • A scheduled rule (EventBridge cron-style), for something like polling a third-party API every hour and saving the result to DynamoDB.
  • An S3 upload event, for something like generating a thumbnail the moment an image lands in a bucket.
  • An SQS queue, where Lambda pulls messages and processes them as a worker.
  • A DynamoDB Stream, which fires whenever a table changes, useful for something like indexing new records into Elasticsearch.

The specific service on the other end changes; the shape doesn't. A Lambda function is a piece of code that runs in response to whatever event you point at it.

Execution environments and concurrency

AWS Lambda (the managed service) and a Lambda function (your code) are different things. When requests to invoke a function arrive in parallel, the Lambda service spins up parallel execution environments, think of them as containers, to handle them.

One invocation never shares an execution environment with another invocation happening at the same time. That's deliberate: it stops one request's context or data from leaking into a concurrent, unrelated request. That's also why concurrency is capped account-wide, see reserved concurrency below.

Once an invocation finishes, though, its execution environment can be reused for the next request to the same function, not a concurrent one, a subsequent one.

Cold starts and hot starts

A cold start walks through downloading code, starting a new execution environment, running init code, then the handler. A hot start skips straight to the handler ExpandA cold start walks through downloading code, starting a new execution environment, running init code, then the handler. A hot start skips straight to the handler

A cold start happens when there's no existing execution environment to reuse. Lambda has to download the function's code, create a fresh execution environment, start the runtime (Node.js, Python, whatever), and run any initialization code, before it can finally run the handler, the actual function logic.

That setup, typically 100ms to 1 second, isn't billed, but it adds directly to the request's latency.

A hot start skips all of that: the code is already downloaded, the environment already exists, and the request goes straight to the handler. Frequent invocations tend to land on hot starts, since Lambda reuses environments rather than tearing them down after every request. But AWS does reclaim idle environments periodically, so even a busy function will land some fraction of its invocations, on the order of a few percent, on a cold start.

Provisioned concurrency is the fix when cold-start latency genuinely can't be tolerated: pay to keep a set number of execution environments warm ahead of time, code downloaded, runtime started, init code already run. Every request against that reserved capacity goes straight to the handler. It costs extra, so it's worth reaching for only where the latency actually matters.

Don't create functions through the console

The AWS console lets you create a function from scratch, from a blueprint, or from a container image (the container still runs as a normal, event-driven Lambda at runtime, it's just how the code got packaged). Fine for a quick experiment. Not fine for anything real: recreating a function by clicking through the console has to be repeated by hand for every account or environment it needs to exist in, and hand-repeated setup is where mistakes creep in. Define functions with infrastructure as code (the Serverless Framework, CDK, whatever) instead, so the same definition deploys identically everywhere.

Reserved concurrency

Caps how many invocations of one function can run at once. An AWS account gets 1,000 concurrent executions by default (raisable via support ticket), shared across every function in the account. Without a per-function cap, one busy function under load can consume the entire account's concurrency and starve everything else.

Set it to what a downstream dependency can survive, not to what AWS allows. A function calling a rate-limited external system should be capped low even if the account has room to spare.

Event source mapping

The configuration that connects an event source, like an SQS queue, to a Lambda function, deciding how messages become invocations. See SQS for the batch size and concurrency details.

The transport-only anti-pattern

A Lambda function that does nothing but forward a payload from one service to another (API Gateway to EventBridge, for example) is pure overhead: execution cost and cold-start latency with no logic behind it. If a direct service integration exists, use it instead. Add the Lambda back only when the request needs to be transformed, not just moved.

Delivery guarantees matter

Different invocation sources come with different guarantees. Step Functions Express Workflows guarantee at-least-once delivery to their targets, meaning a Lambda step can genuinely run more than once for the same logical request. Check the guarantee before assuming a function only ever runs once, that assumption is what idempotency exists to protect against.

Where this showed up

Further Reading and Watching