Function as a Service is the core compute primitive of serverless architecture. The provider manages all infrastructure; developers deploy only code.
Request ──► API Gateway ──► Cold/Warm Start ──► Function Execution ──► Response
│
┌───────┴────────┐
│ Execution Env │
│ ┌────────────┐ │
│ │ Runtime │ │
│ │ (Node.js) │ │
│ ├────────────┤ │
│ │ Your Code │ │
│ │ + Deps │ │
│ └────────────┘ │
│ /tmp storage │
└────────────────┘
Frozen after use,
reused if available
| Feature |
AWS Lambda |
Cloud Functions 2nd Gen |
Azure Functions |
| Max timeout |
15 min |
60 min |
10 min (Consumption) |
| Max memory |
10 GB |
32 GB |
1.5 GB (Consumption) |
| Concurrency |
1 per instance |
Configurable (up to 1000) |
Configurable |
| Min billing |
1 ms |
100 ms |
1 ms |
| Deployment size |
250 MB (zip), 10 GB (container) |
100 MB (source) |
N/A |
| VPC support |
Yes (improved cold start) |
Yes (Serverless VPC) |
Yes (VNet) |
INIT Phase INVOKE Phase SHUTDOWN
├── Extension init ├── Handler called ├── Graceful
├── Runtime init ├── Business logic │ shutdown
├── Function init ├── Return response │ hooks
│ (outside handler) └── Freeze env └── Cleanup
└── ~100-500ms first time ~ms-seconds
Code outside the handler runs once per execution environment and persists across invocations. Use this for database connections and SDK clients.
# Lambda: connection reuse across invocations
import boto3
dynamodb = boto3.resource('dynamodb') # Initialized once
table = dynamodb.Table('orders') # Reused across invocations
def handler(event, context):
# This runs every invocation
return table.get_item(Key={'id': event['order_id']})
| Category |
Sources |
Invocation |
| API |
API Gateway, ALB, Function URL |
Synchronous |
| Storage |
S3, DynamoDB Streams, Kinesis |
Async / Poll-based |
| Messaging |
SQS, SNS, EventBridge |
Async / Poll-based |
| Orchestration |
Step Functions |
Synchronous |
| Schedule |
EventBridge Scheduler |
Asynchronous |
| Streaming |
Kafka (MSK), Kinesis |
Poll-based (batched) |
Synchronous: Client ──► Lambda ──► Response to client
(API Gateway, ALB)
Asynchronous: Event ──► Lambda Service Queue ──► Lambda
(S3, SNS, EventBridge)
Built-in retry: 2 attempts, then DLQ
Poll-based: Lambda polls ──► SQS/Kinesis/DynamoDB Streams
(Batch processing with configurable batch size)
- DLQ (Dead Letter Queue): Capture failed async invocations in SQS/SNS
- Destinations: Route success/failure to Lambda, SQS, SNS, or EventBridge
- Partial batch failure: Report individual failures in SQS/Kinesis batches
- Idempotency: Use Powertools for deduplication via idempotency keys
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: python3.12
Timeout: 30
MemorySize: 256
Tracing: Active
Resources:
OrderFunction:
Type: AWS::Serverless::Function
Properties:
Handler: app.handler
CodeUri: src/
Events:
ApiEvent:
Type: Api
Properties:
Path: /orders
Method: post
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref OrdersTable
OrdersTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: id
Type: String
// Define serverless API with CDK
const ordersTable = new dynamodb.Table(this, 'Orders', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
const orderFn = new lambda.Function(this, 'OrderFunction', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda/'),
environment: { TABLE_NAME: ordersTable.tableName },
});
ordersTable.grantReadWriteData(orderFn);
const api = new apigateway.RestApi(this, 'OrdersApi');
api.root.addResource('orders').addMethod('POST',
new apigateway.LambdaIntegration(orderFn));
- Serverless Framework: Multi-cloud, plugin ecosystem, large community
- SST (Serverless Stack): CDK-based with live Lambda debugging
- Pulumi: General-purpose IaC with serverless support in multiple languages
Standard (default) Express
├── Max duration: 1 year ├── Max duration: 5 min
├── Exactly-once execution ├── At-least-once execution
├── $0.025 per 1K transitions ├── Based on executions + duration
└── Audit/compliance workflows └── High-volume data processing
{
"StartAt": "ValidateOrder",
"States": {
"ValidateOrder": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:validate",
"Next": "CheckInventory",
"Retry": [{ "ErrorEquals": ["TransientError"], "MaxAttempts": 3 }],
"Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "HandleError" }]
},
"CheckInventory": {
"Type": "Choice",
"Choices": [{
"Variable": "$.inStock",
"BooleanEquals": true,
"Next": "ProcessPayment"
}],
"Default": "BackorderItem"
},
"ProcessPayment": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:payment",
"End": true
}
}
}
| Pattern |
Use Case |
Implementation |
| Sequential |
Pipeline processing |
Chain Task states |
| Parallel |
Fan-out/fan-in |
Parallel state with branches |
| Map |
Process collection items |
Map state (inline or distributed) |
| Choice |
Conditional branching |
Choice state with rules |
| Wait |
Delay or schedule |
Wait state (seconds or timestamp) |
| Saga |
Distributed transactions |
Catch + compensating actions |
| Service |
Type |
Scaling |
Billing |
| DynamoDB On-Demand |
Key-value/document |
Instant, automatic |
Per request |
| Aurora Serverless v2 |
Relational |
0.5 ACU increments |
Per ACU-second |
| Firestore |
Document |
Automatic |
Per operation |
| Neon |
PostgreSQL |
Scale to zero |
Per compute-second |
| PlanetScale |
MySQL |
Automatic branching |
Per row read/write |
| Factor |
Impact |
Mitigation |
| Language runtime |
Java/C# slower than Python/Node |
Choose lightweight runtimes |
| Package size |
Larger = slower init |
Tree-shake, minimize deps |
| VPC attachment |
Additional ENI setup time |
Use VPC-only when needed |
| Concurrent burst |
Many cold starts at once |
Provisioned concurrency |
- Lambda: 15 min max; break long tasks into Step Functions workflows
- API Gateway: 29 second integration timeout for synchronous calls
- Solution: Return async job ID, poll or use WebSocket for result
- Payload size: 6 MB sync, 256 KB async for Lambda invocation
- Concurrency: Account-level limits (default 1000 concurrent)
- Ephemeral storage: /tmp limited to 10 GB
- Stateless: No shared memory between invocations
Lambda Cost = Invocations × (Duration × Memory)
$0.20/1M $0.0000166667 per GB-second
- Right-size memory: Use AWS Lambda Power Tuning to find optimal memory
- Reduce duration: Optimize code, use connection pooling, avoid cold paths
- Minimize invocations: Batch SQS messages, filter events at source
- Use ARM (Graviton2): 20% cheaper with comparable or better performance
- Provisioned concurrency: Only for latency-sensitive paths (costs when idle)
- Tiered architecture: Not everything needs to be serverless
- Sustained high throughput (> millions of invocations/hour consistently)
- Long-running processes (> 15 minutes)
- GPU workloads or specialized hardware needs
- When consistent sub-10ms latency is required
| Pattern |
Description |
Implementation |
| API backend |
REST/GraphQL API |
API GW + Lambda + DynamoDB |
| Event processing |
React to cloud events |
S3 trigger + Lambda + SQS |
| Scheduled tasks |
Cron-like execution |
EventBridge Schedule + Lambda |
| Stream processing |
Real-time data pipeline |
Kinesis + Lambda + S3 |
| Web application |
Full-stack serverless |
CloudFront + S3 + API GW + Lambda |
| ETL pipeline |
Data transformation |
Step Functions + Lambda + Glue |
| ChatBot/webhook |
Receive and process webhooks |
Function URL + Lambda |
- FaaS provides event-driven, auto-scaling compute with per-millisecond billing
- Initialize shared resources (DB connections, SDK clients) outside the handler
- Step Functions orchestrate complex workflows with built-in error handling and retries
- Cold starts are the primary latency concern; mitigate with provisioned concurrency and small packages
- Serverless cost scales linearly with usage, making it ideal for variable/low traffic
- Not all workloads fit serverless; evaluate duration, throughput, and latency requirements