You write a function. The cloud provider runs it when triggered — by an HTTP request, a scheduled timer, or an event. You pay only for execution time. No servers to manage, no containers to orchestrate. What Lambda is, how it differs from always-on backends, and when to use it.
6 min read
A traditional backend runs on a server (or container) that's always on — consuming compute capacity and costing money even when no requests arrive. Serverless flips this: you write a function (e.g., a Node.js handler), upload it to your cloud provider, and they run it only when triggered. You pay for the CPU-seconds you actually consume, not for idle capacity.
AWS Lambda is the most mature example. You write:
export const handler = async (event, context) => {
const name = event.queryStringParameters?.name || "World";
return {
statusCode: 200,
body: JSON.stringify({ message: `Hello, ${name}!` }),
};
};Upload it; configure a trigger (e.g., an HTTP API, an S3 upload, a scheduled timer); and AWS runs it whenever that trigger fires. The function executes, you're billed for the milliseconds it ran, and that's it. No servers to patch, no containers to manage, no infrastructure overhead.
| Aspect | Always-on server | Lambda |
|---|---|---|
| Execution | Continuously running | Only when triggered |
| Cost | Per hour (or month) of uptime | Per millisecond of runtime |
| Scaling | Manual (add more servers) | Automatic (AWS provisions as needed) |
| Cold starts | No delay — already running | Delay on first invoke (≈100ms) |
| Duration limit | Unlimited | 15 minutes max (900 seconds) |
| State between invokes | Can maintain in-memory state | Stateless (globals reset) |
Lambda is ideal for bursty workloads (processing user uploads, sending emails) and background jobs. It's awkward for long-running processes (a 10-hour data pipeline) or workloads that need persistent in-memory caches.
Lambda functions run when something triggers them. Common triggers:
Each trigger provides an event — data about what happened — passed to the handler function. The handler processes the event and returns a response (if synchronous) or succeeds silently (if asynchronous).
When you invoke a Lambda for the first time, or after idle time, AWS must:
This takes roughly 100–500ms depending on function size and language — the cold start. Subsequent invocations on the same container reuse it and are much faster.
Cold starts are invisible for user-initiated requests that are okay with a 200–300ms delay. But for something like a WebSocket connection or a financial transaction that expects sub-50ms latency, a cold start is noticeable and bad. Mitigation strategies:
Lambda functions have hard limits:
/tmp)**: 10 GB of temporary storage.Lambda pricing is roughly:
If your function uses 512 MB and runs for 100 ms per request, that's 0.05 GB-seconds per request, or roughly $0.0000008 per request in compute costs.
Lambda is cheap when:
Lambda is expensive when:
A few patterns that matter:
Keep business logic in libraries, not the handler:
// BAD — handler contains the logic
export const handler = async (event) => {
const db = await connectDB();
const user = await db.query("SELECT * FROM users WHERE id=?", [event.userId]);
// ... 50 more lines of logic
};
// GOOD — handler delegates to a library
import { getUserProfile } from "./user-service";
export const handler = async (event) => {
try {
const profile = await getUserProfile(event.userId);
return { statusCode: 200, body: JSON.stringify(profile) };
} catch (error) {
return { statusCode: 500, body: "Error" };
}
};This keeps your code testable — you test getUserProfile with fake databases, and the handler is just a thin orchestration layer.
Reuse database connections across invocations:
let dbPool = null;
const getPool = async () => {
if (!dbPool) {
dbPool = await createPool({ max: 2 }); // Small pool — this is one Lambda instance
}
return dbPool;
};
export const handler = async (event) => {
const pool = await getPool();
const user = await pool.query(...);
// Don't close the pool — reuse it on the next invocation
};The first invocation initializes the pool; later invocations reuse it. This saves connection-setup time and keeps database connection counts manageable.
Serverless seems "free" until you look at the bill. A genuinely busy endpoint — say, 100 requests per second sustained, at 512 MB and 100ms each — works out to about 259 million invocations a month: roughly $52 in request costs plus $216 in compute (12.96 million GB-seconds), around $268/month. A small always-on server handling that same sustained load could easily cost less. Measure and benchmark; don't assume serverless is cheaper just because there's no server to manage — it depends entirely on how sustained the traffic actually is.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. How is Lambda billing different from an always-on server?
2. What is a cold start, and why does it matter?
3. What is Lambda's maximum execution time, and what should you do if you need longer?
4. Why should Lambda business logic be in libraries rather than directly in the handler?
Cloud Computing & Infrastructure