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
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
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.





