5 min read
On this page

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

  1. Right-size memory: Use AWS Lambda Power Tuning to find optimal memory
  2. Reduce duration: Optimize code, use connection pooling, avoid cold paths
  3. Minimize invocations: Batch SQS messages, filter events at source
  4. Use ARM (Graviton2): 20% cheaper with comparable or better performance
  5. Provisioned concurrency: Only for latency-sensitive paths (costs when idle)
  6. 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