---
title: "16 — Lambda and the Serverless Family: Code That Runs Without a Server in Sight"
uid: lambda-serverless
tags: ["aws", "lambda", "cold-start", "roadmap:aws", "serverless", "api-gateway", "eventbridge", "fargate"]
excerpt: "Lambda is the function; the family around it is the architecture — API Gateway and EventBridge for triggers, Lambda@Edge at the edge, Fargate as the container alternative, plus layers, versioning, and the cold start."
date: 2026-08-13T03:28:28+0000
source: https://www.aveshina.my.id/en/blog/lambda-serverless
---

"Functions in the cloud" described Lambda but not the ecosystem around it, and the ecosystem is where the architecture actually lives. The settlement that landed: **Lambda is event-driven code I never provision a server for, and around it sits a family of services that supply the triggers (EventBridge, S3, API Gateway), the edge variant (Lambda@Edge), and the container alternative (Fargate) — and the operational reality of all of it is layers, runtimes, versioning, and the cold start** [1]. The function itself is small; the family is what makes it an architecture.

## Lambda: code triggered by events

AWS Lambda runs my code in response to **events** and manages the compute underneath automatically [1]. I upload a function, wire it to a trigger, and AWS handles provisioning, scaling, and teardown. I pay per request and per millisecond of execution — nothing when the function isn't running. The supported runtimes include Node.js, Python, Java, Go, Ruby, and C#, and a Runtime API lets me bring others [3][4].

The mental shift from EC2 or ECS: I no longer think about instances, clusters, or capacity. I think about **a function, its trigger, and its execution role**. That's the whole unit. The trade-off for that simplicity is a set of constraints — execution time limits, the cold start, a stateless execution environment — which dictate where Lambda is the right tool and where it isn't.

## Creating and invoking functions

A function has a name, a runtime, an **execution role** (an IAM role that grants the function's permissions — read this bucket, write to that table), and the code itself [2]. Once created, it's invoked in one of three ways:

- **Synchronous (push)** — a caller (the CLI, an API Gateway request, another service) invokes it and waits for the response.
- **Asynchronous (event)** — a service emits an event ("S3 object created") and Lambda runs the function; the emitter doesn't wait.
- **Polling (stream-based)** — Lambda itself polls a source (a DynamoDB stream, an SQS queue, Kinesis) and runs the function for each batch.

The first Lambda function I wrote was a few lines of Node.js that read an event and logged it — which is the right starting point. Everything else (IAM, layers, versioning) layers on top of that same shape.

## Layers: sharing dependencies without bloating the package

A **Lambda layer** is a ZIP archive of libraries, custom runtimes, or other dependencies that a function can reference [5]. The layer is extracted to /opt in the execution environment at runtime. The value:

- **Shared code across functions** — a common logging utility, a company SDK, a heavy dependency, all maintained in one layer and attached to many functions.
- **Smaller deployment packages** — the function ZIP holds only the function's own code; the heavy stuff lives in a layer.
- **Versioned and immutable** — each layer version is a distinct artifact, so updating a layer never silently breaks functions pinned to the old one.

The discipline: anything shared across three or more functions, or anything large that changes rarely (a heavy ML inference library), belongs in a layer rather than being vendored into every function package.

## Custom runtimes: any language, via the Runtime API

If my preferred language or version isn't in the managed runtimes, a **custom runtime** fills the gap [6]. It's a Linux executable that handles the conversation with the Lambda service via the Runtime API — receiving events, returning responses. Bring Rust, a specific Node version, an obscure interpreter — the Runtime API is the contract that makes any of them work. Most teams never need this; the supported runtimes cover the vast majority of cases. It exists for the long tail.

## Versioning and aliases: safe deployment

**Versioning** creates immutable, numbered snapshots of a function's code plus configuration [7]. $LATEST is the working copy; publishing creates 1, 2, 3 — each a fixed artifact that never changes. An **alias** is a mutable pointer to a specific version (prod → 3, staging → 4).

The payoff: triggers and downstream services point at the *alias*, not the version. To deploy, I publish a new version, point prod at it (optionally with weighted traffic shifting for canaries), and roll back by repointing the alias. No trigger reconfiguration needed. This is the Lambda analog of immutable infrastructure — the function's identity stays stable while the code behind it changes.

## EventBridge: the serverless event bus

**EventBridge** is the serverless event bus that connects applications using events from my own apps, SaaS services, and other AWS services [8]. It subsumes CloudWatch Events and adds richer routing, schema registries, and cross-account/cross-region delivery. The shape:

- Events are ingested onto a **bus**.
- **Rules** match events by pattern (or on a schedule — cron).
- Matched events are routed to **targets** — a Lambda function, a Step Function, an SQS queue, an API destination.

EventBridge is the backbone of event-driven architectures on AWS. "When an order is placed, emit an event; the inventory service, the notification service, and the analytics service each consume it independently" — that decoupling is EventBridge's job.

## API Gateway: the HTTP front door

For HTTP-triggered Lambda, **API Gateway** is the front door [10]. It accepts HTTP requests, maps them to Lambda invocations, and returns the response — handling throttling, authorization, request/response transformation, and version management along the way. The combo (API Gateway + Lambda) is the canonical serverless web API: no servers to provision, scales to zero when idle, and bills per request.

## Lambda@Edge and Fargate: the variants

Two more family members round out the serverless story:

- **Lambda@Edge** runs Lambda functions at CloudFront edge locations, so the code executes close to the user [11]. Use cases: rewriting URLs, customizing responses, A/B testing at the edge. The latency win comes from geography — the function runs where the user is, not where my region is.
- **Fargate** is the serverless container runtime [9]. It's not Lambda — it runs containers, not functions, and they stay up rather than scaling to zero. But it shares the serverless spirit: I specify CPU/memory per task and AWS runs it on capacity I never manage. When a workload is too long-running, stateful, or complex for Lambda's model but I still don't want to manage EC2, Fargate is the middle ground. (The notes on the ECS/EKS stack cover the full picture.)

## The cold start: the honest constraint

The **cold start** is the latency hit Lambda pays on a function's first invocation after being idle, or after a code or dependency update [12]. AWS has to provision an execution environment, initialize the runtime, and load the code before the handler runs — work that's already done on a warm invocation. Cold starts are most noticeable on larger functions, on less-common runtimes (Java is famously slow to start), and on low-traffic functions that go idle between invocations.

The mitigations I reach for:

- **Right-size the function** — only load what the handler actually needs, lazily initializing heavy clients inside the handler rather than at module top.
- **Provisioned concurrency** — keep a pool of warm environments ready, eliminating cold starts for latency-critical paths (at extra cost).
- **SnapStart (for Java)** — restore from a pre-initialized snapshot instead of initializing from scratch.

The honest framing: most workloads tolerate the occasional cold start fine. It matters for synchronous, latency-critical paths (a user-facing API), and is largely invisible for asynchronous event processing.

```figure
<svg viewBox="0 0 700 300" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="A Lambda function in the center, with triggers flowing in from API Gateway (HTTP), EventBridge (events and schedules), and an S3 bucket (object events). Around the function: four operational dials — Layers, Runtime, Version/Alias, Cold Start. To the side, a Fargate container as the longer-running alternative.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <defs><marker id="la" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#64748b"/></marker></defs>

    <!-- triggers -->
    <rect x="20" y="40" width="140" height="40" rx="8" fill="#e0e7ff" stroke="#6366f1"/><text x="90" y="64" font-size="10" font-weight="700" fill="#1e1b4b" text-anchor="middle">API Gateway (HTTP)</text>
    <rect x="20" y="120" width="140" height="40" rx="8" fill="#fce7f3" stroke="#db2777"/><text x="90" y="144" font-size="10" font-weight="700" fill="#500724" text-anchor="middle">EventBridge (events)</text>
    <rect x="20" y="200" width="140" height="40" rx="8" fill="#dcfce7" stroke="#16a34a"/><text x="90" y="224" font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">S3 / DynamoDB stream</text>

    <!-- Lambda core -->
    <rect x="260" y="80" width="180" height="140" rx="10" fill="#fef9c3" stroke="#ca8a04"/>
    <text x="350" y="105" font-size="13" font-weight="700" fill="#422006" text-anchor="middle">Lambda function</text>
    <text x="350" y="122" font-size="9" fill="#422006" text-anchor="middle">code + execution role</text>
    <text x="350" y="160" font-size="9" fill="#422006" text-anchor="middle">· layers  · runtime</text>
    <text x="350" y="176" font-size="9" fill="#422006" text-anchor="middle">· version / alias</text>
    <text x="350" y="192" font-size="9" fill="#422006" text-anchor="middle">· cold start</text>

    <!-- arrows in -->
    <path d="M160,60 L260,110" stroke="#64748b" stroke-width="1.3" marker-end="url(#la)"/>
    <path d="M160,140 L260,150" stroke="#64748b" stroke-width="1.3" marker-end="url(#la)"/>
    <path d="M160,220 L260,180" stroke="#64748b" stroke-width="1.3" marker-end="url(#la)"/>

    <!-- output -->
    <path d="M440,150 L540,150" stroke="#64748b" stroke-width="1.3" marker-end="url(#la)"/>
    <rect x="540" y="120" width="140" height="60" rx="8" fill="#dbeafe" stroke="#2563eb"/>
    <text x="610" y="145" font-size="11" font-weight="700" fill="#1e3a8a" text-anchor="middle">AWS services</text>
    <text x="610" y="162" font-size="9" fill="#1e3a8a" text-anchor="middle">S3, DynamoDB, SNS…</text>

    <!-- Fargate alternative -->
    <rect x="260" y="240" width="180" height="40" rx="8" fill="#fce7f3" stroke="#db2777"/>
    <text x="350" y="264" font-size="10" font-weight="700" fill="#500724" text-anchor="middle">Fargate — serverless containers</text>
  </g>
</svg>
```

## How I use this

Lambda and its family are my default for event-driven, short-running, stateless work — the glue of an AWS architecture. The decision tree I run: if the work is triggered by an event (an upload, a webhook, a schedule) and finishes in seconds, Lambda is usually right; if the work is long-lived, stateful, or always-on, a container on Fargate or ECS is the better shape, because Lambda's billing and cold start work against always-on loads. When I do use Lambda, I keep functions small and single-purpose, share heavy or common code through layers, deploy through aliases with traffic shifting, and put provisioned concurrency only on the genuinely latency-critical synchronous paths. API Gateway fronts any HTTP-triggered functions; EventBridge carries the internal event flows. The way of thinking I keep is the family, not the function: Lambda is one shape of "no servers," Fargate is another, and picking between them is a question of state, runtime, and traffic pattern — not of which is "more serverless."

## References

[1] Amazon Web Services, "What is AWS Lambda?," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/welcome.html](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html)

[2] Amazon Web Services, "Getting started with Lambda," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html)

[3] Amazon Web Services, "Lambda runtimes," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)

[4] Amazon Web Services, "Building Lambda functions with custom runtimes," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html)

[5] Amazon Web Services, "AWS Lambda layers," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html)

[6] Amazon Web Services, "Lambda custom runtimes," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html)

[7] Amazon Web Services, "Lambda function versioning and aliases," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html)

[8] Amazon Web Services, "What is Amazon EventBridge?," EventBridge User Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)

[9] Amazon Web Services, "AWS Fargate," 2024. [Online]. Available: [https://aws.amazon.com/fargate/](https://aws.amazon.com/fargate/)

[10] Amazon Web Services, "What is Amazon API Gateway?," 2024. [Online]. Available: [https://aws.amazon.com/api-gateway/](https://aws.amazon.com/api-gateway/)

[11] Amazon Web Services, "Lambda@Edge with CloudFront," CloudFront Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html)

[12] Amazon Web Services, "Reducing Lambda cold start times with SnapStart," Lambda Developer Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html)

```quiz
Q: A Lambda function's execution role controls…
- what AWS resources the function may touch (read this bucket, write to that table)
- how much CPU and memory the function gets
correct: 0
explain: The execution role is an IAM role. It grants the function's permissions. Memory/CPU are set in the function configuration, not the role.

Q: Why publish versions and point triggers at an alias instead of at $LATEST?
- So deploys and rollbacks are a re-point of the alias to an immutable version, with no trigger reconfiguration
- Because $LATEST is read-only
correct: 0
explain: Versions are immutable snapshots; aliases are mutable pointers to them. Deploying = publish + repoint the alias; rolling back = repoint to the previous version.

Q: A Lambda layer is best used for…
- shared dependencies or heavy libraries used by multiple functions
- the function's own business logic
correct: 0
explain: Layers hold libraries, custom runtimes, and shared utilities, extracted to /opt. Anything used by three or more functions, or large and rarely changing, belongs in a layer.

Q: The cold start is most painful for…
- synchronous, user-facing, latency-critical invocations
- asynchronous background event processing
correct: 0
explain: Cold starts add latency to a function's first invocation after idle. For user-facing APIs that's visible; for async event processing it usually doesn't matter. Provisioned concurrency is the mitigation for the critical paths.

Q: When is Fargate a better fit than Lambda?
- for long-running, stateful, or always-on workloads that don't fit Lambda's model
- for tiny functions triggered a few times an hour
correct: 0
explain: Fargate runs containers that stay up. Lambda is event-driven and scales to zero. Always-on workloads on Lambda pay for idle and fight the cold start; Fargate is the serverless-shape answer for those.
```
