Serverless Architecture Patterns
Build serverless apps: Lambda function patterns, cold start mitigation, event-driven architecture, AWS SAM/SST, and when serverless is the wrong choice.
Serverless functions (AWS Lambda, Cloudflare Workers, Vercel Edge Functions) run code on-demand without managing servers. They excel for event-driven workloads, APIs with variable traffic, and scheduled jobs. Key trade-offs: no persistent connections, cold starts, and execution time limits.
Use serverless for APIs with spiky or variable traffic, event processing, and scheduled tasks. Avoid for long-running processes, WebSocket servers, or workloads needing persistent in-memory state. Model costs carefully - high-volume constant workloads may be cheaper on reserved instances.
Workflow
- Structure Lambda functions correctlyWell-structured Lambdas initialize shared resources outside the handler for warm starts.
- Mitigate cold startsCold starts (first invocation or scaling) add 100ms-3s latency - critical for user-facing APIs.
- Build event-driven patterns with SQSSQS decouples producers and consumers - Lambda processes messages asynchronously at its own pace.
- Deploy with SST (Ion)SST wraps Pulumi/CDK to make serverless infrastructure-as-code TypeScript-first and developer-friendly.
- Model and optimize serverless costsServerless costs scale with requests - high-traffic apps can be more expensive than EC2.
Output Format
Handler with initialized clients outside, provisioned concurrency for P99 latency SLAs, SQS trigger with batch item failure reporting, SST for infrastructure, Lambda URL over API Gateway for cost.
Examples
SQS batch with partial failure: `export const handler = async (event: SQSEvent) => { const failures: { itemIdentifier: string }[] = []; await Promise.allSettled(event.Records.map(async (record) => { try { await processOrder(JSON.parse(record.body)) } catch (e) { logger.error({ messageId: record.messageId, error: e }); failures.push({ itemIdentifier: record.messageId }) } })); return { batchItemFailures: failures } }`SST with RDS: `const db = new sst.aws.Postgres("Database"); const api = new sst.aws.Function("Api", { handler: "src/api.ts", link: [db] }); // in handler: const dbUrl = Resource.Database.secretArn`Cold start optimization: `const { DynamoDBClient } = await import("@aws-sdk/client-dynamodb"); const client = new DynamoDBClient({}); // top-level await in ESM Lambda - cached across warm invocations`Edge Cases & Gotchas
- Lambda timeout: max 15 minutes. For longer workloads, use Step Functions (orchestrate multiple Lambdas), ECS Fargate tasks, or async processing with SQS
- Lambda and VPC: placing Lambda in a VPC for RDS access adds ~500ms to cold starts (ENI setup). Use RDS Proxy to pool connections and Lambda outside VPC with IAM auth where possible
- Idempotency: Lambda can be invoked more than once for the same event (at-least-once delivery from SQS). Make all Lambda handlers idempotent using the AWS Powertools idempotency decorator or manual deduplication
When to Break These Rules
- WebSocket servers, real-time collaborative apps, long-running ML inference, and always-on TCP connection handlers are poor fits for serverless. Use EC2, ECS, or managed services instead.
Reference Files
AWS Lambda docs: https://docs.aws.amazon.com/lambda/SST docs: https://sst.dev/Lambda Power Tuning: https://github.com/alexcasalboni/aws-lambda-power-tuningCloudflare Workers: https://developers.cloudflare.com/workers/