Module Overview
- Lambda execution model and lifecycle
- Event sources and triggers
- Lambda Layers and custom runtimes
- Cold starts and performance optimization
- VPC configuration and networking
- Concurrency and scaling
- Best practices and production patterns
- Cost optimization strategies
Why Lambda?
No Servers
Auto-Scaling
Pay Per Use
Event-Driven
Lambda Execution Model
Handler Function Structure
The handler is the entry point Lambda calls. Everything outside the handler runs once per container (cold start) and persists across warm invocations — this is the single most important optimization you can make. Think of it like a restaurant kitchen: the init code sets up the stations and heats the ovens (once), while the handler is the cook who plates each individual order.Event Sources and Triggers
Understanding event source types is critical because they determine your error handling strategy, retry behavior, and scaling characteristics. A senior engineer would say: “The invocation type dictates everything downstream — your idempotency requirements, your DLQ strategy, and your concurrency model.”Common Event Formats
Each trigger sends a different event shape. Memorizing these is less important than understanding the pattern: every event source wraps its data differently, and your handler’s first job is to unwrap the payload correctly.Lambda Layers
Layers allow you to share code and dependencies across multiple functions. Think of layers like shared libraries on a Linux system — instead of every application bundling its own copy oflibc, they all reference a single shared installation. In Lambda terms, instead of every function including its own copy of boto3 or requests in its deployment package, they all reference a shared layer at /opt/.
Creating a Lambda Layer
AWS Lambda Powertools
Lambda Powertools is the single most impactful library you can add to any production Lambda function. It provides structured logging, distributed tracing, and custom metrics with minimal boilerplate. If you are not using it, you are writing all of this plumbing yourself (and probably getting it wrong). The Azure equivalent is Azure Functions Extensions; the GCP equivalent is the Functions Framework with OpenTelemetry.Cold Starts and Optimization
Cold starts are the single most discussed topic in serverless computing, and also the most misunderstood. A cold start happens when Lambda must create a new execution environment to handle your request — downloading your code, starting the runtime, and running your initialization code. The restaurant analogy: a cold start is like opening the kitchen from scratch (turning on ovens, prepping stations), while a warm start is an already-running kitchen that just needs to plate the next order. The critical nuance: cold starts affect only a small percentage of invocations in most workloads, but they affect 100% of invocations if your function is rarely called. A function handling 100 requests/second rarely cold-starts. A function handling 1 request/hour cold-starts almost every time.Understanding Cold Starts
Optimization Strategies
Provisioned Concurrency
Provisioned Concurrency (PC) is Lambda’s answer to “I cannot tolerate cold starts.” It pre-initializes execution environments so they are always warm and ready. Think of it as keeping a fleet of taxis idling at the curb — you pay for the idling, but the moment a passenger arrives, the ride starts instantly.VPC Configuration
Putting Lambda in a VPC is one of the most consequential decisions you will make, and one of the most commonly over-applied. The default rule should be: do NOT put Lambda in a VPC unless you must access private resources like RDS, ElastiCache, or internal EC2 instances. Lambda functions outside a VPC can already reach all public AWS services (DynamoDB, S3, SQS) without any VPC overhead. The VPC adds networking complexity, potential NAT Gateway costs ($32/month + data processing charges per AZ), and historically added significant cold start latency (though AWS improved this dramatically in 2019 with Hyperplane ENI sharing). Azure comparison: Azure Functions Premium plan always runs in a VNet (virtual network). GCP Cloud Functions use Serverless VPC Access connectors. All three clouds face the same fundamental tension: serverless wants to be stateless and isolated, but databases live in private networks.VPC Configuration Best Practices
Concurrency and Scaling
Lambda scaling is fundamentally different from EC2 or container scaling. With EC2, you scale by adding more machines (horizontal) or bigger machines (vertical). With Lambda, each concurrent request gets its own isolated execution environment. There is no load balancer — AWS handles request routing internally. The scaling model is beautifully simple in theory, but the account-level concurrency limit creates real production risks that catch teams off guard.Reserved Concurrency
Reserved concurrency is both a guarantee and a limit. It guarantees that this function will always have N execution environments available (even if other functions in the account are maxed out), but it also caps this function at N concurrent executions (even if the account has spare capacity). This dual nature is counterintuitive and a frequent source of production incidents.Error Handling and Retries
Error handling in Lambda is not just about try/catch — it is about understanding that different invocation types have fundamentally different retry behaviors. If you design your error handling for synchronous invocations and then switch to asynchronous, your function will silently retry and potentially process events multiple times. A senior engineer would say: “Every Lambda function must be idempotent because you cannot guarantee exactly-once delivery for any invocation type.”Best Practices
Keep Functions Focused
Minimize Package Size
Use Environment Variables
Implement Idempotency
Set Realistic Timeouts
Monitor Everything
Production Checklist
🎯 Interview Questions
Q1: How do you minimize Lambda cold starts?
Q1: How do you minimize Lambda cold starts?
-
Code level:
- Minimize deployment package size
- Lazy load heavy dependencies
- Initialize SDK clients outside handler
-
Configuration:
- Increase memory (faster CPU = faster init)
- Use compiled languages carefully
- Avoid VPC unless necessary
-
Provisioned Concurrency:
- Pre-warm execution environments
- Eliminate cold starts for critical paths
- Use scheduled scaling for traffic patterns
-
Architecture:
- Keep functions warm with scheduled pings (anti-pattern, prefer PC)
- Use Lambda SnapStart for Java
Q2: How does Lambda scale?
Q2: How does Lambda scale?
- Lambda scales by creating more execution environments
- Burst: 500-3,000 concurrent executions immediately
- After burst: 500 additional per minute
- Account limit: 1,000 default (can increase)
- Function reserved concurrency: up to account limit
- Provisioned concurrency: pre-warmed instances
Q3: Lambda vs EC2 vs ECS - when to use each?
Q3: Lambda vs EC2 vs ECS - when to use each?
- Short-lived, event-driven workloads
- Unpredictable/spiky traffic
- < 15 minutes execution
- No server management needed
- Long-running containers
- Microservices architecture
- Consistent traffic patterns
- Need more control than Lambda
- Maximum control/customization
- Specialized hardware needs
- Persistent workloads
- Legacy applications
Q4: How do you handle secrets in Lambda?
Q4: How do you handle secrets in Lambda?
- Hardcode secrets in code
- Store secrets in environment variables
- Log secrets
Q5: Explain Lambda@Edge vs Lambda
Q5: Explain Lambda@Edge vs Lambda
- Runs at CloudFront edge locations
- Lower latency (closer to users)
- Limited: 128MB memory, 5s timeout (viewer), 30s (origin)
- Use cases: URL rewrite, A/B testing, auth, headers
- Runs in a single region
- Full capabilities: 10GB memory, 15min timeout
- More triggers and integrations
- Even faster, cheaper than Lambda@Edge
- 2MB code, 1ms timeout
- Simple header manipulation only
🧪 Hands-On Lab
Create Basic Lambda
Add Dependencies with Layers
Configure VPC Access
Implement Error Handling
Optimize Performance
Interview Deep-Dive
Your team's Lambda-based order processing pipeline handles 500 requests/second normally, but during flash sales traffic spikes to 5,000 requests/second and you start seeing throttling errors. The business says orders are being lost. Walk me through your diagnosis and fix.
Your team's Lambda-based order processing pipeline handles 500 requests/second normally, but during flash sales traffic spikes to 5,000 requests/second and you start seeing throttling errors. The business says orders are being lost. Walk me through your diagnosis and fix.
- First, understand the math. At 500 req/s with an average duration of 200ms, we need 100 concurrent executions (500 x 0.2). At 5,000 req/s, we need 1,000 concurrent executions. The default account limit is 1,000, and our burst limit (depending on region) is 500-3,000 immediate concurrent environments. So we are hitting the account concurrency ceiling during the spike.
- Immediate fix — request a concurrency limit increase. This is a soft limit. I would request an increase to 5,000-10,000 via the AWS Service Quotas console. Approval typically takes 1-3 business days, so this is not an in-the-moment fix.
- Short-term mitigation — add an SQS queue in front of Lambda. Instead of API Gateway invoking Lambda directly (synchronous, throttled requests return 429 to the customer), route orders through SQS. API Gateway writes to SQS (which has virtually unlimited throughput), and Lambda polls SQS with a batch size of 10. This decouples ingestion from processing. The customer gets a 202 Accepted immediately, and orders are never lost because SQS retains messages for up to 14 days. The trade-off: orders are now processed asynchronously, so the response cannot include the order confirmation — you need a notification mechanism (WebSocket, polling, or email).
- Set reserved concurrency on the order processor. Reserve 800 of the 1,000 account limit for this function so other non-critical functions (analytics, logging) cannot steal its capacity during the spike. This guarantees capacity but caps other functions at 200.
- For future flash sales, use Provisioned Concurrency with scheduled scaling. If the sale starts at 9 AM, schedule PC to ramp to 500 at 8:45 AM and back to 0 at 11 PM. This eliminates cold starts during the critical window. At 512 MB memory, 500 PC for 14 hours costs roughly $11 for that day — cheap insurance for a flash sale.
- Long-term architecture — evaluate whether Lambda is the right tool. At sustained 5,000 req/s, Lambda costs roughly 0.0000166667/GB-s). The same workload on Fargate with 10 tasks at 1 vCPU/2 GB costs around $1,200/month. If flash sales happen weekly, Fargate is dramatically cheaper for the steady-state, and Lambda handles the overflow via a hybrid architecture.
order_id, so reprocessing the same order is a no-op, and (2) enable ReportBatchItemFailures on the SQS event source mapping so only the individual failed messages retry, not the entire batch.What impresses interviewers: Showing the cost math (concurrent executions formula, Lambda vs Fargate crossover), knowing the SQS buffering pattern, and immediately addressing idempotency without being prompted. Candidates who only say “increase the limit” miss the architectural thinking interviewers are looking for.You are migrating a monolithic REST API (currently on EC2) to Lambda behind API Gateway. The API has 40 endpoints, connects to RDS PostgreSQL, and some endpoints take up to 30 seconds for complex report generation. How do you approach this migration?
You are migrating a monolithic REST API (currently on EC2) to Lambda behind API Gateway. The API has 40 endpoints, connects to RDS PostgreSQL, and some endpoints take up to 30 seconds for complex report generation. How do you approach this migration?
- Do not migrate everything at once. Start with the strangler fig pattern: put an ALB in front of both the old EC2 monolith and the new Lambda functions, then migrate endpoints one at a time. Route
/api/v1/ordersto Lambda while/api/v1/reportsstill hits EC2. This lets you validate each endpoint in production with real traffic before cutting over. - The 30-second report endpoints cannot go to Lambda behind API Gateway. API Gateway has a hard 29-second timeout that cannot be increased. For these endpoints, you have three options: (1) move the report generation to an asynchronous pattern — the API returns a
202 Acceptedwith areport_id, a Step Functions workflow generates the report, and the client polls or receives a webhook when done; (2) use Lambda Function URLs instead of API Gateway (no 29-second limit, just the Lambda 15-minute limit); (3) keep those endpoints on ECS/Fargate. Option 1 is the cleanest architecture. Option 2 is the quickest migration path but sacrifices API Gateway features like throttling, caching, and WAF integration. - RDS connection pooling is the critical challenge. Each Lambda execution environment opens its own database connection. At 100 concurrent Lambda invocations, you have 100 connections to PostgreSQL. During a traffic spike to 1,000 concurrent, you saturate the RDS max_connections limit (typically ~800 for a db.r5.large). Use RDS Proxy ($0.015/vCPU-hour) to pool connections — Lambda connects to the proxy, which maintains a stable pool of 50-100 actual database connections. Without RDS Proxy, this migration will cause cascading database failures.
- Cold starts matter for a customer-facing API. Python/Node.js functions with RDS Proxy in a VPC will add 200-500ms on cold starts. For the most latency-sensitive endpoints (login, checkout), use Provisioned Concurrency. For less critical endpoints (profile updates, settings), let cold starts happen — users will not notice an occasional extra 300ms.
- Migrate in this order: stateless read endpoints first (low risk, easy to validate), then stateless write endpoints (need idempotency), then stateful/complex endpoints last. Keep the reporting endpoints on Fargate permanently — Lambda is the wrong tool for 30-second synchronous operations.
MaxConnectionsPercent setting — the default of 100% means the proxy will use up to the database’s full max_connections, which defeats the purpose if multiple proxies or pools exist.What impresses interviewers: Knowing the API Gateway 29-second hard limit (not configurable), immediately identifying RDS connection exhaustion as the biggest risk, and proposing the strangler fig pattern instead of a big-bang migration. Mentioning RDS Proxy and its per-function pooling behavior shows production experience.Your Lambda function processes S3 uploads (image thumbnailing). It works perfectly in testing but in production, some images fail silently -- no error logs, no DLQ messages, and the original images remain unprocessed. How do you investigate?
Your Lambda function processes S3 uploads (image thumbnailing). It works perfectly in testing but in production, some images fail silently -- no error logs, no DLQ messages, and the original images remain unprocessed. How do you investigate?
- Silent failures in S3-triggered Lambda usually mean the function was never invoked. S3 event notifications are asynchronous. If the notification itself fails to deliver to Lambda, there is no DLQ because the failure happens before Lambda is involved. First, check: is the S3 event notification configuration correct? In the S3 bucket properties, verify the event type (s3:ObjectCreated:*), the prefix/suffix filters, and the Lambda function ARN. A common mistake is setting a prefix filter of
uploads/but the files are uploaded toupload/(no trailing ‘s’). - Check for Lambda permission issues. S3 needs permission to invoke your Lambda function. If the resource-based policy on the Lambda function does not include
s3:InvokeFunctionfrom the specific bucket ARN, the invocation silently fails. Runaws lambda get-policy --function-name my-functionand verify the S3 principal and bucket ARN are listed. If the policy was set up via the console but the bucket was recreated (different ARN), the policy is stale. - Check for filename encoding issues. S3 event notifications URL-encode the object key. A file named
my photo (1).jpgarrives asmy+photo+%281%29.jpg. If your function does not callurllib.parse.unquote_plus()on the key, the S3 GetObject call fails with a NoSuchKey error. But here is the subtle part: if your error handling catches the exception and returns successfully (no re-raise), Lambda considers the invocation successful. No DLQ, no retry, no error metric. The image is silently skipped. - Check for concurrency throttling. If the function hit the account concurrency limit, S3 async invocations retry with backoff for up to 6 hours. If the function is still throttled after 6 hours, the event is dropped. Check the
ThrottlesCloudWatch metric for the function during the time window when images failed. - Check for event notification delivery limits. S3 event notifications can silently fail if the destination Lambda function does not exist, the region is wrong, or the function is in a failed state. S3 does not log these failures anywhere obvious — you need CloudTrail with data events enabled on the bucket (which costs $0.10 per 100,000 events) to see the notification delivery attempts.
- The fix: add a reconciliation process. Never rely solely on event-driven processing for critical workloads. Run a scheduled Lambda (every 15 minutes) that lists objects in the source bucket, compares against the thumbnails bucket, and reprocesses any missing items. This catch-all pattern costs almost nothing and prevents silent data loss.
Runtime.ExitError or no log at all if the kill happens before the error handler runs. Check the MaxMemoryUsed metric in CloudWatch — if it equals MemorySize, the function is being OOM-killed. Increase memory to 1024 MB or 2048 MB and add a file size check at the start of the handler to reject images above a safe threshold.What impresses interviewers: Systematically working through the failure modes from “event never arrived” to “event arrived but processing failed silently.” Knowing about S3 key URL-encoding, the 6-hour retry window, and the reconciliation pattern shows you have debugged real S3-Lambda pipelines. The OOM-kill scenario (no logs, no error) is a classic production gotcha that only people with hands-on experience know about.Your company wants to deploy Lambda functions across 3 regions for a global API with sub-100ms latency. The functions read from DynamoDB and write to S3. How do you architect this, and what are the consistency trade-offs?
Your company wants to deploy Lambda functions across 3 regions for a global API with sub-100ms latency. The functions read from DynamoDB and write to S3. How do you architect this, and what are the consistency trade-offs?
- Use DynamoDB Global Tables for multi-region reads. Global Tables replicate data across regions with typically sub-second replication lag. Each region’s Lambda reads from its local replica, achieving single-digit-millisecond DynamoDB read latency. This is the critical enabler for sub-100ms global latency. The trade-off: Global Tables use eventual consistency for cross-region replication, so a write in us-east-1 may not be visible in eu-west-1 for 200-1000ms. If your API needs read-your-writes consistency, you must either route that user’s subsequent reads to the same region (using latency-based routing with session affinity) or accept the consistency window.
- S3 replication for multi-region writes. Use S3 Cross-Region Replication (CRR) to replicate objects written in one region to others. CRR typically completes within 15 minutes for most objects. If the application needs to read the object immediately after writing, the reading Lambda must read from the same region that wrote it, or use S3 Transfer Acceleration for faster cross-region access.
- Deploy with a multi-region CI/CD pipeline. Use AWS CodePipeline or a tool like Serverless Framework with multi-region deployment stages. Deploy to a canary region first (e.g., ap-southeast-1 with lowest traffic), validate health metrics for 10 minutes, then deploy to the remaining regions. A bad deployment to all 3 regions simultaneously is a global outage.
- Route 53 latency-based routing for global distribution. Create latency-based DNS records pointing to each region’s API Gateway. Users are automatically routed to the nearest healthy region. Add health checks that test the full stack (API Gateway through Lambda through DynamoDB) so Route 53 can failover if one region degrades.
- The hidden cost trap: 3x everything. Three regions means 3x Lambda invocations (billed per region), 3x DynamoDB Global Tables (write capacity is charged in every replica region), 3x NAT Gateways if using VPC, 3x API Gateway. A setup that costs 7,000-8,000/month in three regions after accounting for replication traffic. Model the full cost before committing.
- Conflict resolution is the hardest problem. With DynamoDB Global Tables, if two users update the same item in different regions simultaneously, the “last writer wins” policy applies based on the timestamp. For most applications (user profiles, preferences), this is fine. For financial transactions or inventory counts, this is dangerous. For those workloads, designate one region as the primary writer and use the others as read replicas only.