Serverless Architecture
FaaS Deep Dive
Function as a Service is the core compute primitive of serverless architecture. The provider manages all infrastructure; developers deploy only code.
Execution Model
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
Provider Comparison
| 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) |
Execution Lifecycle
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']})
Event Sources and Triggers
AWS Lambda Event Sources
| 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) |
Invocation Patterns
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)
Error Handling
- 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
Serverless Frameworks
AWS SAM (Serverless Application Model)
# 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
AWS CDK (Cloud Development Kit)
// 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));
Other Frameworks
- 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
Step Functions (Workflow Orchestration)
State Machine Types
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
Common Patterns
{
"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
}
}
}
Step Functions Patterns
| 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 |
Serverless Databases
| 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 |
Limitations and Mitigations
Cold Starts
| 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 |
Timeouts
- 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
Other Constraints
- 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
Cost Optimization
Pricing Levers
Lambda Cost = Invocations × (Duration × Memory)
$0.20/1M $0.0000166667 per GB-second
Optimization Strategies
- 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
When Serverless Is Not Cost-Effective
- 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
Serverless Patterns
| 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 |
Key Takeaways
- 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