When Lambda scales a function or retires an idle instance, it creates a new execution environment: download code, initialize the runtime, run your init code, then handle the first request. That startup is the cold start, and its cost depends on package size, runtime, and how much work you do at module scope.
Before you start
You should understand Lambda basics such as handlers and invocation. This article focuses on latency and reuse; it assumes a Node.js-style handler.
Step-by-step walkthrough
Step 1: Shrink the deployment package
Cold-start time grows with the size of the code and dependencies that must be loaded. Tree-shake or bundle only what the handler uses, avoid pulling in large SDKs wholesale, and prefer the lightweight runtime clients. A smaller package means faster download and initialization.
Step 2: Initialize once, reuse across invocations
Code outside the handler runs once per execution environment and is reused by later invocations. Create clients, load configuration and warm caches at module scope, and guard lazy initialization so concurrent warm invocations do not duplicate the work.
Step 3: Reserve capacity for latency-critical paths
Provisioned concurrency keeps environments initialized and ready, removing cold starts for the traffic it covers. It costs money, so apply it to interactive endpoints and leave background or batched functions on on-demand scaling.
Worked scenario
The client is created once and reused by every warm invocation.
let client;
export const handler = async (event) => {
client ??= await createClient();
return client.handle(event);
};Walk through the example
The first invocation creates client and pays the connection cost; subsequent invocations on the same environment reuse it because the module state persists. The ??= guard keeps the creation idempotent if two invocations race. This moves work out of the per-request path, which is the same reason heavy imports are avoided.
Common mistake
Creating a new database or HTTP client inside the handler on every invocation, which adds latency and connection churn. Another is assuming a warm environment is guaranteed: it can be recycled at any time, so never store request-specific data in module state.
Verify the behavior
Check the CloudWatch Init Duration metric for cold-start time and the Duration for handler time. Invoke repeatedly and confirm later invocations skip initialization. Measure latency before and after enabling provisioned concurrency on the same function.
Interview exercise
A function reconnects to the database on every call. Why is that slow, and what is the fix?
Answer and reasoning
Each call pays the connection and authentication cost, and it opens more connections than needed. Move the client to module scope so warm invocations reuse it, guarded by a lazy initializer. If connections still churn across many environments, put a connection proxy in front, because concurrency times a per-environment pool can exceed the database’s connection limit.
Continue learning
Compare invocation behavior in Lambda execution and Lambda retries. Read the AWS Lambda cold start documentation and try the AWS interview questions.