← Perspectives

Cloud Engineering

AWS: Serverless at Scale

Production patterns for Lambda, Step Functions, and event-driven architectures—lessons from scaling serverless in regulated enterprises.Perspective

Serverless isn't new anymore. Lambda has been generally available for nearly a decade, and most organizations have at least experimented with it. What's changed is our understanding of where serverless excels, where it struggles, and how to build production systems that leverage its strengths while mitigating its limitations.

This article shares patterns we've developed building serverless applications for clients in banking, insurance, and healthcare—environments where reliability, security, and auditability aren't optional.

When Serverless Makes Sense

Not every workload belongs on Lambda. After years of building serverless applications, we've developed clear criteria for when to recommend serverless versus containers or traditional compute.

✓ Event-Driven Processing

File uploads triggering processing, queue consumers, webhook handlers—workloads with natural event boundaries are ideal Lambda candidates.

✓ Variable/Unpredictable Load

Workloads that spike dramatically then go quiet. Paying for idle containers doesn't make sense when Lambda scales to zero.

✓ Glue Code & Orchestration

Connecting services, transforming data between systems, API backends that primarily coordinate other services.

✓ Scheduled Tasks

Cron jobs, batch processing, report generation—tasks that run periodically rather than continuously.

When to Consider Alternatives

Serverless isn't the answer for everything. We recommend containers (ECS/EKS) or traditional compute when:

  • Consistent high throughput: If you're processing millions of requests per hour consistently, reserved containers are often more cost-effective
  • Long-running processes: Lambda's 15-minute timeout is a hard limit—batch jobs exceeding this need different architecture
  • Stateful applications: Applications requiring persistent connections (WebSockets at scale, database connection pooling) can be challenging
  • Cold start sensitivity: Sub-100ms latency requirements for every request may conflict with cold start realities

The Hybrid Reality

Most production architectures we build are hybrid—Lambda for event processing and API endpoints, containers for long-running services, managed services (RDS, ElastiCache) for data. The key is choosing the right tool for each component, not dogmatically adopting one approach.

Lambda Production Patterns

Getting Lambda functions working is easy. Getting them working reliably at scale in regulated environments requires attention to patterns that aren't obvious from tutorials.

The Cold Start Reality

Cold starts remain Lambda's most discussed limitation. Here's what actually matters in production:

Runtime Typical Cold Start With VPC Mitigation Options
Python 200-400ms +100-200ms Minimize imports, lazy loading
Node.js 200-400ms +100-200ms Bundle optimization, tree shaking
Java 3-6 seconds +100-200ms SnapStart, GraalVM native
.NET 500ms-2s +100-200ms Native AOT, minimal APIs

For synchronous API endpoints where users wait for responses, cold starts matter. For asynchronous processing where work flows through queues, they're often irrelevant—a 500ms delay in processing a file upload is rarely noticed.

Provisioned Concurrency: When It's Worth It

Provisioned Concurrency keeps Lambda instances warm, eliminating cold starts. We use it for latency-sensitive API endpoints in production—but only after measuring actual cold start impact. For many workloads, the cost isn't justified.

Connection Management

Database connections are the most common Lambda scaling pain point. Each Lambda instance maintains its own connection, and at scale, you can easily exhaust connection pools.

Lambda Database Connection Patterns

✗ Problem: Direct Connections Lambda 1 Lambda 2 Lambda N RDS (max 100 conn) 1000 Lambdas = 1000 connections 💥 ✓ Solution: RDS Proxy Lambda 1 Lambda 2 Lambda N RDS Proxy RDS (pooled) Connection pooling handled ✓ Other Options DynamoDB (no connection limits) • Aurora Serverless v2 (auto-scaling) • ElastiCache for caching

Error Handling and Retries

Lambda's built-in retry behavior differs between invocation types, and getting this wrong causes either data loss or infinite loops:

  • Synchronous (API Gateway): No automatic retries—the caller handles retry logic
  • Asynchronous (S3, SNS): Two automatic retries with exponential backoff, then to dead-letter queue
  • Stream-based (Kinesis, DynamoDB Streams): Retries until success or record expiration—can block the entire shard

The Poison Message Problem

A malformed message in a Kinesis stream will block all subsequent messages on that shard until the record expires (up to 7 days). Always configure BisectBatchOnFunctionError and MaximumRetryAttempts with a dead-letter queue for stream processing.

Step Functions for Complex Workflows

When business logic requires multiple steps, conditional branching, or human approval workflows, Step Functions provides orchestration that would be painful to implement in Lambda alone.

When to Use Step Functions

We reach for Step Functions when workflows involve:

  • Multi-step processes: Document processing pipelines, order fulfillment, claim adjudication
  • Long-running workflows: Processes that span hours or days (Step Functions can wait up to a year)
  • Human-in-the-loop: Approval workflows, exception handling requiring manual intervention
  • Parallel processing: Fan-out/fan-in patterns where multiple tasks run concurrently
  • Retry and error handling: Complex error handling with different retry strategies per step

Document Processing Workflow Example

Start Extract OCR/IDP Validate Business Rules Valid? Yes Process Update Systems End No Review Human Task

Express vs. Standard Workflows

Step Functions offers two workflow types with very different characteristics:

Characteristic Standard Express
Max Duration 1 year 5 minutes
Execution Guarantee Exactly-once At-least-once
Pricing Per state transition Per execution + duration
Best For Long-running, audit trails needed High-volume, short-duration

For regulated industries, Standard workflows are typically preferred—the exactly-once guarantee and full execution history are valuable for audit trails. Express workflows work well for high-volume event processing where idempotency is already handled.

Event-Driven Architecture Patterns

Serverless architectures are inherently event-driven. Getting event flow right is critical for reliability and maintainability.

EventBridge as the Backbone

For new architectures, we use Amazon EventBridge as the central event bus. It provides:

  • Schema registry: Documented event structures that producers and consumers agree on
  • Content-based filtering: Consumers subscribe only to events they care about
  • Archive and replay: Re-process historical events for recovery or testing
  • Cross-account delivery: Events flow between accounts in multi-account architectures

The Event Schema Contract

In regulated environments, we treat event schemas as contracts. Changes require versioning, backward compatibility checks, and consumer notification. EventBridge's schema registry enforces this discipline—events that don't match the schema are rejected.

SQS for Reliable Processing

When processing must be reliable and order matters less than completion, SQS provides durability that EventBridge alone doesn't:

  • Visibility timeout: Messages reappear if not processed, ensuring nothing is lost
  • Dead-letter queues: Failed messages are preserved for investigation
  • FIFO queues: When ordering matters (within message groups)
  • Backpressure handling: Queues absorb traffic spikes that would overwhelm downstream systems

Observability in Serverless

Debugging distributed serverless applications requires different approaches than traditional monoliths. Our standard observability stack includes:

Structured Logging

Every Lambda function logs in JSON format with correlation IDs that trace requests across services. CloudWatch Logs Insights makes querying these logs practical at scale.

// Every log entry includes context for tracing { "level": "INFO", "timestamp": "2024-01-15T10:23:45.123Z", "correlationId": "abc-123-def", "requestId": "lambda-req-456", "service": "payment-processor", "message": "Payment validated", "paymentId": "pay-789", "amount": 150.00 }

X-Ray for Distributed Tracing

AWS X-Ray traces requests across Lambda functions, API Gateway, and AWS services. For regulated industries, the ability to show exactly how a request flowed through the system—with timing at each step—is valuable for both debugging and compliance.

CloudWatch Metrics and Alarms

Beyond the built-in Lambda metrics (invocations, errors, duration), we publish custom metrics for business-relevant measurements: documents processed, payments completed, validation failures. Alarms trigger on both technical failures and business anomalies.

Building for Production

Serverless removes infrastructure management, but it doesn't remove the need for production-grade practices:

  • Infrastructure as Code: Every Lambda, every IAM role, every EventBridge rule defined in CloudFormation or CDK
  • CI/CD pipelines: Automated testing, deployment, and rollback capabilities
  • Environment parity: Dev, staging, and production environments that mirror each other
  • Disaster recovery: Multi-region deployment for critical workloads, tested failover procedures

The serverless model changes how we build, but not whether we need these capabilities. If anything, the distributed nature of serverless makes disciplined practices more important, not less.