TL;DR
Serverless doesn't mean there are no servers — it means you don't manage them. You deploy functions and configure managed services, AWS handles the capacity, and you pay only for what you actually use. The win is operational simplicity and automatic scaling. The cost is a new set of constraints: cold starts, enforced statelessness, deeper vendor coupling, and observability that works differently from a traditional server. This post lays out the building blocks, a reference pattern, and the tradeoffs so you can decide where serverless fits.
What Serverless Actually Means
Four properties define serverless, and each one is a tradeoff in disguise:
- No server management — no instances to patch, scale, or keep alive. AWS runs the infrastructure.
- Event-driven — code runs in response to events (a request, an upload, a schedule), not as an always-on process.
- Pay-per-use — you're billed for actual invocations and duration, not idle capacity. Zero traffic, near-zero cost.
- Automatic scaling — the platform scales from zero to thousands of concurrent executions without you configuring an autoscaler.
Core AWS Building Blocks
| Service | Role in a serverless app |
|---|---|
| <strong>Lambda</strong> | Runs your code in response to events — the compute layer. |
| <strong>API Gateway</strong> | Front door for HTTP APIs: routing, auth, throttling, request validation. |
| <strong>DynamoDB</strong> | Serverless NoSQL database with single-digit-millisecond reads and on-demand capacity. |
| <strong>S3</strong> | Object storage for static assets, uploads, and hosting SPA front-ends. |
| <strong>EventBridge</strong> | Event bus and scheduler for decoupling services and async workflows. |
| <strong>Step Functions</strong> | Orchestrates multi-step workflows with retries, branching, and state — without you writing glue code. |
The art of serverless is composing these managed services so you write as little undifferentiated plumbing as possible.
A Reference Web/API Pattern
The most common serverless pattern is a thin API in front of functions and a managed database:
# A minimal SAM/serverless-style definition of one API route
Resources:
GetOrderFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs20.x
MemorySize: 256
Timeout: 10
Policies:
- DynamoDBReadPolicy:
TableName: orders
Events:
GetOrder:
Type: Api
Properties:
Path: /orders/{id}
Method: get
The function is tiny, stateless, and does one job. Scaling, availability, and capacity are the platform's problem.
Synchronous Front, Asynchronous Back
A healthy serverless app keeps the request path short and pushes slow work to the background:
- Synchronous path — client → API Gateway → Lambda → DynamoDB → response. Keep this fast; the user is waiting.
- Asynchronous path — the request Lambda emits an event to EventBridge (or a queue) and returns immediately. A worker Lambda picks it up and does the heavy lifting.
This split keeps latency low and lets each part scale independently.
Benefits
- Operational simplicity — no OS patching, no capacity planning, no autoscaling groups to tune.
- Cost efficiency for spiky or low traffic — you pay per request, so idle time is nearly free. Great for bursty, unpredictable, or early-stage workloads.
- Fast iteration — small, single-purpose functions deploy quickly and independently.
- Elastic scaling — handles sudden traffic spikes without pre-provisioning.
Tradeoffs
Serverless is not free of friction. Know these before you commit:
- Cold starts — the first invocation of an idle function pays an initialization penalty. Mitigate latency-sensitive paths with provisioned concurrency and lean dependencies.
- Statelessness — functions keep no reliable local state between invocations. All state must live in DynamoDB, S3, or another external store.
- Vendor lock-in — deep use of managed services ties you to the provider. The coupling is often worth the productivity, but it's a real cost at migration time.
- Observability — there's no server to SSH into. You rely on structured logs, distributed tracing (X-Ray), and metrics. Debugging distributed, event-driven flows takes more upfront instrumentation.
- Execution limits — Lambda has timeout and payload limits; long-running or heavy-compute jobs may fit containers or Step Functions better.
When Serverless Fits — and When It Doesn't
| Good fit | Reconsider |
|---|---|
| Spiky, unpredictable, or low-baseline traffic | Steady, high, constant traffic (always-on compute may be cheaper) |
| Event-driven processing and glue logic | Long-running or stateful workloads |
| APIs that map cleanly to short handlers | Latency-critical paths that can't tolerate any cold start |
| Teams that want to minimize ops | Workloads needing specialized hardware or OS-level control |
Common Mistakes to Avoid
- Ignoring cold starts on user-facing paths. Measure p99 latency; use provisioned concurrency and trim dependencies where it matters.
- Storing state in the function. Local variables don't survive across invocations reliably. Externalize all state.
- Doing slow work synchronously. Don't make the user wait for an email send or a report build — push it to an async worker via EventBridge or a queue.
- Skipping observability. Without structured logs and tracing, a distributed serverless app is nearly impossible to debug. Instrument from day one.
- Over-granting IAM. It's tempting to give functions broad permissions. Scope each function's execution role to exactly what it needs.
FAQ
Does serverless mean there are no servers?
No — there are still servers, you just don't provision, patch, or scale them. AWS manages the underlying infrastructure and runs your code on demand, so from your perspective there's no server to operate. You configure functions and managed services, and the platform handles capacity.
What is a cold start and how do I reduce it?
A cold start is the extra latency when Lambda has to initialize a new execution environment for an idle function, including loading your code and runtime. You reduce it by keeping deployment packages and dependencies small, choosing a lighter runtime, and using provisioned concurrency to keep a pool of instances warm for latency-sensitive endpoints.
When should I not use serverless?
Reconsider serverless for steady high-volume workloads where always-on compute is cheaper, for long-running or heavily stateful jobs that bump into execution limits, and for latency-critical paths that can't tolerate any cold start. Workloads needing specialized hardware or OS-level control are also a poor fit. In those cases containers or dedicated instances often serve better.
How do I handle state in a serverless app?
Keep functions stateless and store all state in external managed services — DynamoDB for application data, S3 for objects and files, and ElastiCache or similar for low-latency shared caches. Never rely on local variables or the local filesystem persisting between invocations, because each invocation may run in a fresh environment.