Everyone wants to go serverless these days. No servers to manage! Pay only for what you use! Scale infinitely!
But serverless isnt magic. It has tradeoffs. And for some workloads, its actually more expensive and complex than a simple server.
The Decision Framework
When Serverless Shines
1. Spiky, Unpredictable Traffic
If your traffic looks like a heartbeat monitor, serverless makes sense.
2. Event-Driven Workloads
Perfect for Lambda:
- S3 file uploads → Process image
- SQS messages → Send notification
- API Gateway → Handle webhook
- DynamoDB streams → Sync data
// S3 trigger - process uploaded images
export const handler = async (event: S3Event) => {
for (const record of event.Records) {
const bucket = record.s3.bucket.name;
const key = record.s3.object.key;
await resizeImage(bucket, key);
await generateThumbnail(bucket, key);
}
};
3. Microservices / APIs
Small, focused functions work great:
// Each function does one thing
// api/users/create.ts
export const handler = async (event: APIGatewayEvent) => {
const user = await createUser(JSON.parse(event.body));
return { statusCode: 201, body: JSON.stringify(user) };
};
// api/users/get.ts
export const handler = async (event: APIGatewayEvent) => {
const user = await getUser(event.pathParameters.id);
return { statusCode: 200, body: JSON.stringify(user) };
};
When Serverless Doesnt Work
1. Long-Running Processes
Lambda has a 15-minute limit. If your job takes longer, you need something else.
2. Steady, High-Volume Traffic
At some point, Lambda costs more than EC2:
Lambda pricing:
- $0.20 per 1M requests
- $0.0000166667 per GB-second
100M requests/month at 256MB, 200ms average:
= $20 (requests) + $83 (compute) = $103/month
EC2 t3.medium running 24/7:
= ~$30/month
If you're consistently processing millions of requests, do the math.
3. Stateful Applications
Lambda functions are stateless. Each invocation starts fresh.
// ❌ This doesn't work in Lambda
let cache = new Map(); // Gone after each invocation
export const handler = async (event) => {
// Cache is empty every time!
if (cache.has(event.id)) {
return cache.get(event.id);
}
// ...
};
You need external state (Redis, DynamoDB) for everything.
4. Cold Starts Matter
For user-facing APIs where latency matters, cold starts can be painful. Provisioned concurrency helps but adds cost.
The Hybrid Approach
You dont have to choose just one:
Use serverless for what its good at, containers for everything else.
Decision Checklist
Go Serverless if:
- [ ] Traffic is spiky or unpredictable
- [ ] Workloads are event-driven
- [ ] Functions complete in under 15 minutes
- [ ] You want zero server management
- [ ] Cost per request is acceptable
Use Containers/EC2 if:
- [ ] Traffic is steady and predictable
- [ ] Processes run longer than 15 minutes
- [ ] You need persistent connections (WebSockets)
- [ ] Cold start latency is unacceptable
- [ ] Cost math favors always-on compute
Further Reading
Serverless is a tool, not a religion. Use it where it makes sense. And do the cost math before committing - sometimes a boring EC2 instance is exactly what you need.
