
Serverless computing allows developers to build and run applications without managing servers. The cloud provider handles all infrastructure — provisioning, scaling, patching, and maintenance. Developers write functions, configure triggers, and the platform takes care of the rest.
Serverless computing allows developers to build and run applications without managing servers. The cloud provider handles all infrastructure — provisioning, scaling, patching, and maintenance. Developers write functions, configure triggers, and the platform takes care of the rest.
The name "serverless" is misleading — servers are still involved. But the developer never thinks about them. This shift from infrastructure management to pure application logic is transforming how applications are built and deployed.
┌─────────────────────┐
│ Event Source │
│ (HTTP, Queue, Timer,│
│ Storage, etc.) │
└──────────┬──────────┘
│ Trigger
▼
┌──────────────────────────────────────────────────┐
│ Serverless Function │
│ ┌────────────────────────────────────────────┐ │
│ │ 1. Cold Start (if needed) │ │
│ │ - Provision sandbox │ │
│ │ - Initialize runtime (Node, Python) │ │
│ │ - Load dependencies │ │
│ ├────────────────────────────────────────────┤ │
│ │ 2. Execute handler function │ │
│ │ 3. Return response │ │
│ └────────────────────────────────────────────┘ │
│ Auto-scales horizontally │
│ from 0 to N concurrent executions │
└──────────────────────────────────────────────────┘
// AWS Lambda handler
exports.handler = async (event) => {
const { name } = JSON.parse(event.body);
const response = {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: `Hello, ${name}!`,
timestamp: new Date().toISOString()
})
};
return response;
};
| Aspect | Traditional (servers) | Serverless |
|---|---|---|
| Server management | Required (patching, updates) | None |
| Scaling | Manual or auto-scaling groups | Automatic, from 0 to 1000s |
| Billing | Per-hour/instance (idle cost) | Per-execution + per-memory (no idle cost) |
| Cold starts | N/A (always on) | 100ms-1s for initial invocation |
| Max execution | Unlimited | Timeout (15 min Lambda, 10 min Cloud Functions) |
| State | Persistent (in-memory state) | Stateless by design |
| Deployment | Full server or container | Single function |
| Granularity | Monolith or service | Single function |
| Event Source | Provider | Use Case |
|---|---|---|
| HTTP (API Gateway) | AWS, Azure, GCP | REST APIs, webhooks |
| Database Change (CDC) | DynamoDB Streams, CosmosDB Change Feed, Firestore | Sync data, trigger workflows |
| File Upload | S3, Blob Storage, Cloud Storage | Image processing, file validation |
| Message Queue | SQS, Queue Storage, Pub/Sub | Decoupled async processing |
| Stream | Kinesis, Event Hubs, Pub/Sub | Real-time data processing |
| Schedule | CloudWatch Events, Timer, Scheduler | Cron jobs, batch processing |
| SES, SendGrid | Email processing | |
| IoT | IoT Core, Event Grid | Device telemetry processing |
# AWS SAM template — Function triggered by multiple events
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
OrderProcessor:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: orders.handler
Runtime: nodejs20.x
Events:
HttpPost:
Type: Api
Properties:
Path: /orders
Method: POST
QueueConsumer:
Type: SQS
Properties:
Queue: !GetAtt OrderQueue.Arn
BatchSize: 10
StreamProcessor:
Type: DynamoDB
Properties:
Stream: !GetAtt OrdersTable.StreamArn
StartingPosition: TRIM_HORIZON
┌──────────┐
│ User │
│ Uploads │
└────┬─────┘
▼
┌─────────────────┐
│ S3 Bucket │
│ (upload/raw) │
└────────┬────────┘
│ trigger
▼
┌─────────────────┐
│ Lambda │
│ (process.ts) │
│ │
│ 1. Read image │
│ 2. Resize │
│ 3. Compress │
│ 4. Extract │
│ metadata │
└────────┬────────┘
│
┌───────┴───────┐
▼ ▼
┌────────────┐ ┌──────────────┐
│ S3 Bucket │ │ DynamoDB │
│ (processed)│ │ (metadata) │
└────────────┘ └──────────────┘
// process.ts — Image processing Lambda
import { S3 } from '@aws-sdk/client-s3';
import { DynamoDB } from '@aws-sdk/client-dynamodb';
import sharp from 'sharp';
const s3 = new S3();
const dynamo = new DynamoDB();
const SUPPORTED_FORMATS = ['image/jpeg', 'image/png', 'image/webp'];
export const handler = async (event: S3Event) => {
const results = await Promise.allSettled(
event.Records.map(async (record) => {
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
// Validate file type
const headResult = await s3.headObject({ Bucket: bucket, Key: key });
if (!SUPPORTED_FORMATS.includes(headResult.ContentType!)) {
console.log(`Unsupported format: ${headResult.ContentType}`);
return;
}
// Read original
const { Body } = await s3.getObject({ Bucket: bucket, Key: key });
const buffer = await Body!.transformToByteArray();
// Process: resize to multiple sizes
const sizes = [
{ suffix: 'thumbnail', width: 150, height: 150 },
{ suffix: 'medium', width: 800, height: 600 },
{ suffix: 'large', width: 1920, height: 1080 },
];
for (const size of sizes) {
const processed = await sharp(buffer)
.resize(size.width, size.height, { fit: 'cover' })
.webp({ quality: 80 })
.toBuffer();
const outputKey = key.replace('raw', `processed/${size.suffix}`);
await s3.putObject({
Bucket: bucket.replace('-raw', '-processed'),
Key: outputKey,
Body: processed,
ContentType: 'image/webp',
});
}
// Store metadata
await dynamo.putItem({
TableName: 'ImageMetadata',
Item: {
imageId: { S: key },
originalSize: { N: buffer.byteLength.toString() },
formats: { SS: ['thumbnail', 'medium', 'large'] },
uploadedAt: { S: new Date().toISOString() },
},
});
})
);
const failed = results.filter(r => r.status === 'rejected');
if (failed.length > 0) {
throw new Error(`${failed.length} images failed processing`);
}
return { processed: results.length - failed.length, failed: failed.length };
};
When a function is idle, the platform reclaims its resources. The next invocation requires:
Total cold start: 100ms to 2s depending on runtime and package size.
| Runtime | Cold Start (median) | Cold Start (p99) |
|---|---|---|
| Python | 200ms | 500ms |
| Node.js | 250ms | 600ms |
| Go | 100ms | 300ms |
| .NET | 500ms | 2s |
| Java | 800ms | 3s |
| Rust | 100ms | 250ms |
| Strategy | How It Works | Cost Impact |
|---|---|---|
| Provisioned Concurrency (Lambda) | Keep N instances always warm | Pay for warm instances |
| SnapStart (Lambda) | Pre-initialize and snapshot runtime | Minimal extra cost |
| Keep-warm pings | Periodic invocations every 5 minutes | Negligible |
| Lighter runtime | Use Node.js/Python/Go instead of Java | No extra cost |
| Minimize dependencies | Smaller deployment packages | No extra cost |
Most serverless platforms charge based on:
Scenario: API handling 1M requests/month, 200ms avg execution, 512MB memory
Traditional (t3.small, $0.0208/hr):
$0.0208 × 730 hours = $15.18/month (always on)
Serverless (Lambda):
Requests: 1M × $0.20/1M = $0.20
Duration: 1M × 0.2s × 512MB/1024MB × $0.0000166667/GB-s = $1.67
Total: $1.87/month
Savings: ~87% at low traffic
Breakeven point: At ~45M requests/month, traditional becomes cheaper
# Serverless cost optimization checklist
cost_optimization:
- "Right-size memory: 128MB may be sufficient, but 1769MB costs 14x more"
- "Use AWS Compute Optimizer for memory recommendations"
- "Minimize function duration (optimize code, use async where possible)"
- "Use reserved concurrency only when needed"
- "Delete unused functions and resources"
- "Use Lambda SnapStart for Java/.NET to reduce duration"
- "Leverage Lambda@Edge or CloudFront Functions for edge processing"
| Service | Provider | Key Feature |
|---|---|---|
| AWS Lambda | AWS | Largest ecosystem, 15 min timeout |
| Azure Functions | Microsoft | Deep Office 365 / Teams integration |
| Cloud Functions | Python-first, eventarc integration | |
| Cloudflare Workers | Cloudflare | Edge-based, <1ms cold starts, 30MB scripts |
| Service | Type | Serverless Feature |
|---|---|---|
| DynamoDB | Key-value + Document | On-demand capacity, auto-scaling |
| Aurora Serverless | Relational (MySQL/PostgreSQL) | Auto-scaling compute capacity |
| Cosmos DB | Multi-model | Serverless throughput mode |
| Firestore | Document | Auto-scaling, real-time sync |
| Neon | Serverless PostgreSQL | Branching, auto-suspend |
| Service | Use Case |
|---|---|
| S3 / Blob / Cloud Storage | File storage, event triggers |
| ElastiCache Serverless | In-memory caching |
| SQS / Queue Storage | Message queuing |
| EventBridge / Event Grid | Event bus |
Process multiple items in parallel:
SQS Queue → Lambda → SNS Topic → Lambda (email)
→ Lambda (SMS)
→ Lambda (push)
→ Lambda (logging)
// Order saga with compensation
async function createOrder(event) {
const { orderId, userId, items, total } = event;
try {
await reserveInventory(items);
await processPayment(userId, total);
await createShipment(orderId);
await sendConfirmation(userId);
return { status: 'completed', orderId };
} catch (error) {
// Compensating transactions (undo)
await cancelShipment(orderId);
await refundPayment(userId, total);
await releaseInventory(items);
throw error;
}
}
Handle traffic spikes by buffering requests:
API Gateway → SQS (buffer) → Lambda (process)
↑
Process at your own pace
// Store events, derive current state
interface OrderEvent {
type: 'CREATED' | 'ITEM_ADDED' | 'PAID' | 'SHIPPED' | 'CANCELLED';
orderId: string;
timestamp: string;
data: Record<string, any>;
}
// Function: store event
exports.recordEvent = async (event: OrderEvent) => {
await dynamo.putItem({
TableName: 'OrderEvents',
Item: marshal(event)
});
// Don't modify order state directly
};
// Function: rebuild state from events
exports.getOrderState = async (orderId: string) => {
const events = await queryEvents(orderId);
return events.reduce((state, event) => applyEvent(state, event), initialState);
};
// Structured logging for serverless
const logger = {
info: (message: string, context?: object) => {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'INFO',
message,
aws_request_id: context.awsRequestId,
function_name: context.functionName,
...context
}));
},
error: (message: string, error?: Error) => {
console.error(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'ERROR',
message,
error_name: error?.name,
error_message: error?.message,
stack: error?.stack,
}));
}
};
// X-Ray tracing
import { AWS } from '@aws-sdk/client-lambda';
import { captureAWS } from 'aws-xray-sdk-core';
const lambda = captureAWS(new AWS.Lambda());
exports.handler = async (event) => {
const segment = new Segment('process_order');
// ... function logic with automatic subsegment tracking
};
Key metrics to monitor:
| Scenario | Why Serverless Is Not Ideal | Alternative |
|---|---|---|
| Long-running processes (>15 min) | Execution timeout | Containers, VMs |
| Stateful applications | Stateless by design | Stateful services (WebSocket API, Redis) |
| Constant high traffic (>100 req/s sustained) | Costs exceed provisioned capacity | Auto-scaling containers |
| Low-latency requirements (<10ms) | Cold start overhead | Provisioned concurrency, edge functions |
| Complex orchestration | Function composition is hard | Step Functions, workflows |
| Platform-specific features | Limited runtime capabilities | Full OS access (containers) |
Serverless architecture is ideal for:
Start with serverless for:
Migrate to containers when:
Serverless is not the answer to everything — but for the right workloads, it dramatically reduces operational overhead and development time.
No approved comments are visible yet. New community replies may wait for moderation.