You are viewing a preview of this lesson. Sign in to start learning
Back to Mastering AWS

Serverless & Lambda

Lambda at scale, cold starts, concurrency, and serverless patterns beyond hello world

Serverless & Lambda

Master serverless computing with AWS Lambda using free flashcards and spaced repetition practice. This lesson covers Lambda functions, event-driven architecture, and scaling patternsβ€”essential concepts for building modern cloud applications without managing servers.

Welcome to Serverless Computing πŸš€

Imagine running your code without ever provisioning, configuring, or managing a single server. That's the promise of serverless computing. AWS Lambda revolutionized cloud architecture by letting you execute code in response to eventsβ€”and you only pay for the compute time you consume, measured in milliseconds.

In this lesson, you'll learn how to leverage Lambda for real-world applications, understand execution models, and avoid common pitfalls that catch even experienced developers.

Core Concepts

What is Serverless? πŸ’‘

Serverless doesn't mean "no servers"β€”it means you don't manage them. AWS handles provisioning, scaling, patching, and availability. You focus entirely on your code.

Key characteristics:

  • Event-driven execution: Code runs in response to triggers (HTTP requests, file uploads, database changes)
  • Automatic scaling: From zero to thousands of concurrent executions
  • Pay-per-use: Charged only for actual execution time (billed in 1ms increments)
  • Stateless: Each invocation is independent; state must be stored externally
Traditional vs Serverless Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           TRADITIONAL SERVER                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  πŸ–₯️ Server running 24/7                         β”‚
β”‚  πŸ’° Pay for idle time                           β”‚
β”‚  πŸ“Š Manual scaling                              β”‚
β”‚  πŸ”§ OS patching & management                    β”‚
β”‚  ⚠️ Over/under provisioning risk                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           SERVERLESS (LAMBDA)                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  ⚑ Runs only when triggered                     β”‚
β”‚  πŸ’° Pay only for execution time                 β”‚
β”‚  πŸ“Š Auto-scales to demand                       β”‚
β”‚  πŸ”§ Zero infrastructure management              β”‚
β”‚  βœ… Perfect capacity every time                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

AWS Lambda Fundamentals πŸ”Ί

A Lambda function is a stateless compute service that executes your code in response to events.

Core components:

ComponentDescriptionExample
HandlerEntry point function AWS invokeslambda_handler(event, context)
EventJSON input containing trigger dataS3 object details, API request body
ContextRuntime information (request ID, timeout, memory)context.get_remaining_time_in_millis()
RuntimeExecution environment (language version)Python 3.11, Node.js 18.x, Java 17
Execution RoleIAM permissions for AWS service accessRead S3, write DynamoDB

Basic Python Lambda function:

import json

def lambda_handler(event, context):
    # Extract data from event
    name = event.get('name', 'World')
    
    # Business logic
    message = f'Hello, {name}!'
    
    # Return response
    return {
        'statusCode': 200,
        'body': json.dumps({'message': message})
    }

Node.js Lambda function:

exports.handler = async (event) => {
    const name = event.name || 'World';
    
    const response = {
        statusCode: 200,
        body: JSON.stringify({
            message: `Hello, ${name}!`
        })
    };
    
    return response;
};

πŸ’‘ Tip: Lambda supports multiple languages: Python, Node.js, Java, Go, Ruby, .NET, and custom runtimes.

Lambda Execution Model ⚑

Understanding the lifecycle of a Lambda invocation is crucial for optimization:

LAMBDA INVOCATION LIFECYCLE

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ COLD START (first invocation or after idle)   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                β”‚
β”‚  1️⃣ Download code package                      β”‚
β”‚     β”‚ (from S3)                               β”‚
β”‚     ↓                                          β”‚
β”‚  2️⃣ Start execution environment                β”‚
β”‚     β”‚ (container, runtime)                    β”‚
β”‚     ↓                                          β”‚
β”‚  3️⃣ Initialize (run code outside handler)      β”‚
β”‚     β”‚ (imports, connections, config)          β”‚
β”‚     ↓                                          β”‚
β”‚  4️⃣ Invoke handler function                    β”‚
β”‚     β”‚ (your business logic)                   β”‚
β”‚     ↓                                          β”‚
β”‚  5️⃣ Return response                            β”‚
β”‚                                                β”‚
β”‚  ⏱️ Total: 100-1000ms depending on setup       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ WARM START (reused environment)               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                β”‚
β”‚  βœ… Environment already initialized             β”‚
β”‚     β”‚                                          β”‚
β”‚     ↓                                          β”‚
β”‚  4️⃣ Invoke handler function                    β”‚
β”‚     β”‚ (your business logic)                   β”‚
β”‚     ↓                                          β”‚
β”‚  5️⃣ Return response                            β”‚
β”‚                                                β”‚
β”‚  ⏱️ Total: 1-10ms (much faster!)               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Cold start optimization strategies:

  1. Keep functions small: Smaller deployment packages download faster
  2. Minimize initialization: Move one-time setup outside handler but keep it lightweight
  3. Use Provisioned Concurrency: Pre-warm execution environments for consistent latency
  4. Choose compiled languages: Java/C# have longer cold starts than Python/Node.js

Example: Optimized initialization

import boto3
import os

## Initialize OUTSIDE handler (runs once per container)
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['TABLE_NAME'])

def lambda_handler(event, context):
    # Handler runs on every invocation (use pre-initialized resources)
    user_id = event['userId']
    
    response = table.get_item(Key={'userId': user_id})
    
    return {
        'statusCode': 200,
        'body': json.dumps(response.get('Item', {}))
    }

⚠️ Common mistake: Initializing resources inside the handler causes redundant work on every invocation.

Event Sources & Triggers 🎯

Lambda integrates with 50+ AWS services as event sources:

Event SourceUse CaseInvocation Type
API GatewayREST/HTTP APIs, webhooksSynchronous
S3Process uploaded files (images, logs)Asynchronous
DynamoDB StreamsReact to database changesStream (poll-based)
SQSQueue processing, background jobsStream (poll-based)
EventBridgeScheduled tasks, custom eventsAsynchronous
KinesisReal-time data streamingStream (poll-based)
SNSPub/sub notificationsAsynchronous
ALBApplication Load Balancer targetsSynchronous

Invocation types:

  • Synchronous: Caller waits for response (API Gateway, ALB)
  • Asynchronous: Lambda queues event, returns immediately (S3, SNS)
  • Stream-based: Lambda polls source and processes batches (SQS, Kinesis, DynamoDB Streams)
EVENT-DRIVEN ARCHITECTURE FLOW

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   User      β”‚
β”‚  (Mobile)   β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚ 1. Upload photo
       ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        2. S3 Event         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     S3      │─────────────────────────────→│   Lambda     β”‚
β”‚   Bucket    β”‚                              β”‚  (Resize)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                              β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                                    β”‚ 3. Store metadata
                                                    ↓
                                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                             β”‚  DynamoDB    β”‚
                                             β”‚    Table     β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                                    β”‚ 4. Stream change
                                                    ↓
                                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                             β”‚   Lambda     β”‚
                                             β”‚  (Notify)    β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                                    β”‚ 5. Send notification
                                                    ↓
                                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                             β”‚     SNS      β”‚
                                             β”‚   Topic      β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Memory, Timeout & Concurrency Configuration πŸ“Š

Memory allocation (128 MB to 10,240 MB):

  • Determines CPU power proportionally
  • More memory = faster execution = potentially lower cost
  • Testing optimal memory is crucial for cost/performance
## Example: Memory impacts execution time
## 512 MB: 5 seconds = $0.000000833 Γ— 5000ms = $0.00417
## 1024 MB: 2.5 seconds = $0.000001667 Γ— 2500ms = $0.00417
## Same cost, but 2Γ— faster response! ⚑

Timeout (1 second to 15 minutes maximum):

  • Default: 3 seconds
  • Set based on expected execution time
  • Function terminates if timeout exceeded

⚠️ Critical: API Gateway has 29-second timeoutβ€”Lambda timeout must be less!

Concurrency:

  • Account limit: 1,000 concurrent executions per region (default, can increase)
  • Reserved concurrency: Guarantee capacity for critical functions
  • Provisioned concurrency: Keep functions warm (eliminates cold starts)
CONCURRENCY MODEL

Scenario: 1,000 requests/second, 100ms execution time

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Required Concurrency = RPS Γ— Duration         β”‚
β”‚  = 1,000 req/s Γ— 0.1s = 100 concurrent        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  Request Flow:
  
  T=0s    100 invocations start
  T=0.1s  100 complete, next 100 start
  T=0.2s  100 complete, next 100 start
  ...
  
  Steady state: 100 concurrent executions

Practical Examples

Example 1: API Backend with API Gateway 🌐

Building a serverless REST API for a task management application.

Architecture:

Client β†’ API Gateway β†’ Lambda β†’ DynamoDB

Lambda function (Python):

import json
import boto3
import uuid
from datetime import datetime

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Tasks')

def lambda_handler(event, context):
    http_method = event['httpMethod']
    
    if http_method == 'GET':
        # List all tasks
        response = table.scan()
        return {
            'statusCode': 200,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps(response['Items'])
        }
    
    elif http_method == 'POST':
        # Create new task
        body = json.loads(event['body'])
        
        task = {
            'taskId': str(uuid.uuid4()),
            'title': body['title'],
            'status': 'pending',
            'createdAt': datetime.utcnow().isoformat()
        }
        
        table.put_item(Item=task)
        
        return {
            'statusCode': 201,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps(task)
        }
    
    elif http_method == 'PUT':
        # Update task status
        body = json.loads(event['body'])
        task_id = event['pathParameters']['taskId']
        
        table.update_item(
            Key={'taskId': task_id},
            UpdateExpression='SET #status = :status',
            ExpressionAttributeNames={'#status': 'status'},
            ExpressionAttributeValues={':status': body['status']}
        )
        
        return {
            'statusCode': 200,
            'body': json.dumps({'message': 'Task updated'})
        }
    
    else:
        return {
            'statusCode': 405,
            'body': json.dumps({'error': 'Method not allowed'})
        }

Key patterns:

  • Single Lambda handling multiple HTTP methods (monolithic approach)
  • Alternative: One Lambda per endpoint (microservices approach)
  • DynamoDB for sub-10ms latency
  • Proper HTTP status codes and headers

πŸ’‘ Production tip: Use API Gateway request validation to reject invalid requests before Lambda invocation (saves cost).

Example 2: S3 Image Processing Pipeline πŸ“Έ

Automatic thumbnail generation when images are uploaded.

Workflow:

User uploads image.jpg β†’ S3 Bucket
                           ↓ (S3 Event)
                       Lambda Function
                           ↓
                    Resize to 200x200
                           ↓
                  Save to S3 /thumbnails/

Lambda function (Python with Pillow):

import boto3
import os
from PIL import Image
from io import BytesIO

s3 = boto3.client('s3')

def lambda_handler(event, context):
    # Extract S3 event information
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key']
    
    # Skip if already a thumbnail
    if key.startswith('thumbnails/'):
        return {'statusCode': 200, 'body': 'Already a thumbnail'}
    
    # Download original image
    response = s3.get_object(Bucket=bucket, Key=key)
    image_content = response['Body'].read()
    
    # Resize image
    image = Image.open(BytesIO(image_content))
    image.thumbnail((200, 200), Image.LANCZOS)
    
    # Save to buffer
    buffer = BytesIO()
    image.save(buffer, format=image.format)
    buffer.seek(0)
    
    # Upload thumbnail
    thumbnail_key = f'thumbnails/{key}'
    s3.put_object(
        Bucket=bucket,
        Key=thumbnail_key,
        Body=buffer,
        ContentType=response['ContentType']
    )
    
    return {
        'statusCode': 200,
        'body': f'Thumbnail created: {thumbnail_key}'
    }

Deployment considerations:

  • Layer usage: Package Pillow as a Lambda Layer (reduces deployment size)
  • Memory: Image processing needs 512-1024 MB for decent performance
  • Timeout: Set 1-2 minutes for large images
  • Error handling: Handle corrupted images gracefully

⚠️ Watch out: S3 events are asynchronousβ€”Lambda retries failures automatically. Make operations idempotent!

Example 3: Scheduled Data Processing with EventBridge ⏰

Daily report generation running at 6 AM UTC.

EventBridge Rule:

{
  "schedule": "cron(0 6 * * ? *)",
  "target": "arn:aws:lambda:us-east-1:123456789012:function:DailyReport"
}

Lambda function (Node.js):

const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();
const ses = new AWS.SES();

exports.handler = async (event) => {
    try {
        // Query yesterday's orders
        const yesterday = new Date();
        yesterday.setDate(yesterday.getDate() - 1);
        const dateKey = yesterday.toISOString().split('T')[0];
        
        const params = {
            TableName: 'Orders',
            IndexName: 'DateIndex',
            KeyConditionExpression: 'orderDate = :date',
            ExpressionAttributeValues: {
                ':date': dateKey
            }
        };
        
        const result = await dynamodb.query(params).promise();
        
        // Calculate metrics
        const totalOrders = result.Items.length;
        const totalRevenue = result.Items.reduce(
            (sum, item) => sum + item.amount, 0
        );
        
        // Generate report email
        const emailParams = {
            Source: 'reports@example.com',
            Destination: {
                ToAddresses: ['management@example.com']
            },
            Message: {
                Subject: {
                    Data: `Daily Report - ${dateKey}`
                },
                Body: {
                    Text: {
                        Data: `Total Orders: ${totalOrders}\nRevenue: $${totalRevenue.toFixed(2)}`
                    }
                }
            }
        };
        
        await ses.sendEmail(emailParams).promise();
        
        return {
            statusCode: 200,
            body: JSON.stringify({
                message: 'Report sent successfully',
                metrics: { totalOrders, totalRevenue }
            })
        };
        
    } catch (error) {
        console.error('Report generation failed:', error);
        throw error;
    }
};

Cron expression breakdown:

cron(0 6 * * ? *)
     β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
     β”‚ β”‚ β”‚ β”‚ β”‚ └─ Year (not required)
     β”‚ β”‚ β”‚ β”‚ └─── Day of week (? = any)
     β”‚ β”‚ β”‚ └───── Month (1-12)
     β”‚ β”‚ └─────── Day of month (1-31)
     β”‚ └───────── Hour (0-23) - 6 AM
     └─────────── Minute (0-59) - 0 minutes

πŸ’‘ Pro tip: Use CloudWatch Logs Insights to analyze execution patterns and identify optimization opportunities.

Example 4: DynamoDB Stream Processing πŸ“Š

Real-time analytics on database changes.

Use case: Track user activity score as items are added/updated/deleted.

import boto3
from decimal import Decimal

dynamodb = boto3.resource('dynamodb')
scores_table = dynamodb.Table('UserScores')

def lambda_handler(event, context):
    for record in event['Records']:
        event_name = record['eventName']  # INSERT, MODIFY, REMOVE
        
        if event_name in ['INSERT', 'MODIFY']:
            # Get new image
            new_item = record['dynamodb']['NewImage']
            user_id = new_item['userId']['S']
            action = new_item['action']['S']
            
            # Calculate points
            points = {
                'post_created': 10,
                'comment_added': 5,
                'like_given': 1
            }.get(action, 0)
            
            # Update user score atomically
            scores_table.update_item(
                Key={'userId': user_id},
                UpdateExpression='ADD score :points',
                ExpressionAttributeValues={':points': Decimal(points)}
            )
            
        elif event_name == 'REMOVE':
            # Handle deletions if needed
            pass
    
    return {
        'statusCode': 200,
        'body': f'Processed {len(event["Records"])} records'
    }

Stream processing characteristics:

  • Batch processing: Lambda receives multiple records (1-10,000)
  • At-least-once delivery: Design for idempotency
  • Ordered processing: Within a shard, records are ordered
  • Automatic retries: Failed batches are retried

⚠️ Critical pattern: Use ADD operation in DynamoDB for atomic counters (handles duplicates gracefully).

Common Mistakes ⚠️

1. Ignoring Cold Starts in Latency-Sensitive Apps

Problem: API endpoints with 2-second cold starts frustrate users.

Solution:

  • Use Provisioned Concurrency for critical endpoints
  • Keep deployment packages under 50 MB
  • Choose interpreted languages (Python, Node.js) for faster starts
  • Consider Lambda@Edge for global distribution

2. Not Setting Appropriate Timeouts

Problem: Default 3-second timeout causes premature function termination.

Example:

## ❌ BAD: No timeout consideration
def lambda_handler(event, context):
    # This might take 5 seconds!
    result = heavy_computation()
    return result  # Function terminated at 3 seconds
## βœ… GOOD: Check remaining time
import time

def lambda_handler(event, context):
    remaining = context.get_remaining_time_in_millis()
    
    if remaining < 5000:  # Need at least 5 seconds
        return {'error': 'Insufficient time remaining'}
    
    result = heavy_computation()
    return result

Fix: Set timeout to 2Γ— expected execution time with monitoring.

3. Creating Resources Inside Handler

Problem: Initializing AWS clients on every invocation wastes time and money.

## ❌ BAD: Initialize every time
def lambda_handler(event, context):
    s3 = boto3.client('s3')  # Repeated work!
    response = s3.get_object(Bucket='my-bucket', Key='file.txt')
    return response
## βœ… GOOD: Initialize once
import boto3

s3 = boto3.client('s3')  # Reused across invocations

def lambda_handler(event, context):
    response = s3.get_object(Bucket='my-bucket', Key='file.txt')
    return response

4. Insufficient IAM Permissions (or Too Many)

Problem: Following the principle of least privilege is often overlooked.

// ❌ BAD: Overly permissive
{
  "Effect": "Allow",
  "Action": "s3:*",
  "Resource": "*"
}
// βœ… GOOD: Specific permissions
{
  "Effect": "Allow",
  "Action": ["s3:GetObject", "s3:PutObject"],
  "Resource": "arn:aws:s3:::my-specific-bucket/*"
}

5. Not Handling Async Errors Properly

Problem: S3/SNS/EventBridge invocations retry automaticallyβ€”failures can cause infinite loops.

Solution:

  • Configure Dead Letter Queue (DLQ) for failed events
  • Set maximum retry attempts (default is 2)
  • Implement idempotency checks
## βœ… Idempotent S3 processing
import hashlib

processed_cache = {}  # In production, use DynamoDB

def lambda_handler(event, context):
    key = event['Records'][0]['s3']['object']['key']
    etag = event['Records'][0]['s3']['object']['eTag']
    
    # Create unique identifier
    cache_key = hashlib.md5(f'{key}:{etag}'.encode()).hexdigest()
    
    if cache_key in processed_cache:
        return {'statusCode': 200, 'body': 'Already processed'}
    
    # Process file
    process_file(key)
    
    processed_cache[cache_key] = True
    return {'statusCode': 200}

6. Deploying Large Dependencies

Problem: 250 MB deployment limit includes dependencies.

Solution:

  • Use Lambda Layers for shared libraries
  • Remove unused packages
  • Use lightweight alternatives (e.g., boto3 already included in Python runtime)
## Check package size before deployment
du -sh ./deployment-package
## If > 50MB, consider layers or trimming

7. Not Monitoring Costs

Problem: Inefficient functions with high memory/long duration cost more than expected.

Monitoring:

  • AWS Cost Explorer: Track Lambda spending
  • CloudWatch Logs Insights: Analyze duration patterns
  • Lambda Power Tuning: Find optimal memory configuration
## Power Tuning finds sweet spot
128 MB: 5000ms execution = $0.00042
512 MB: 1500ms execution = $0.00025  ← Optimal!
1024 MB: 1000ms execution = $0.00033

πŸ’‘ Did you know? AWS Lambda Power Tuning is an open-source tool that automatically tests different memory configurations to find the most cost-effective setting for your function.

Key Takeaways 🎯

βœ… Serverless means no infrastructure managementβ€”focus on code, AWS handles everything else

βœ… Cold starts matterβ€”optimize initialization, use Provisioned Concurrency for critical paths

βœ… Event-driven architectureβ€”Lambda excels at responding to S3, DynamoDB, API Gateway, and 50+ other triggers

βœ… Pay only for execution timeβ€”measured in milliseconds, making it cost-effective for sporadic workloads

βœ… Memory = CPUβ€”more memory means proportionally more CPU; test to find optimal cost/performance

βœ… Stateless by designβ€”use S3, DynamoDB, ElastiCache for state persistence

βœ… Concurrent execution scales automaticallyβ€”from 0 to 1,000s, but understand account limits

βœ… Idempotency is criticalβ€”especially for async/retry scenarios (S3, DynamoDB Streams)

βœ… Monitoring and loggingβ€”CloudWatch Logs/Metrics are essential for debugging and optimization

βœ… Security firstβ€”least-privilege IAM roles, encrypt environment variables, use VPC when needed

πŸ“‹ Quick Reference Card: Lambda Essentials

Max execution time15 minutes
Memory range128 MB - 10,240 MB
Deployment package50 MB zipped, 250 MB unzipped
Concurrency limit1,000 (default, can increase)
Environment variables4 KB total
Ephemeral storage (/tmp)512 MB - 10 GB
Invocation typesSynchronous, Asynchronous, Stream-based
Pricing (us-east-1)$0.20 per 1M requests + $0.0000166667 per GB-second
Free tier1M requests + 400,000 GB-seconds/month

🧠 Memory Aid: "SCALE" for Lambda Success

  • Stateless designβ€”store state externally
  • Cold startsβ€”optimize initialization
  • Asynchronous patternsβ€”use DLQs and retries
  • Least privilegeβ€”minimal IAM permissions
  • Events everywhereβ€”trigger from 50+ sources

πŸ“š Further Study

  1. AWS Lambda Developer Guide: https://docs.aws.amazon.com/lambda/latest/dg/welcome.html - Comprehensive official documentation with best practices

  2. Serverless Framework: https://www.serverless.com/framework/docs - Popular infrastructure-as-code tool for deploying Lambda functions

  3. AWS Lambda Power Tuning: https://github.com/alexcasalboni/aws-lambda-power-tuning - Open-source tool to optimize memory/cost configuration

πŸš€ Next Steps: Try building a simple serverless API using Lambda + API Gateway + DynamoDB. Start with the AWS Free Tierβ€”1 million Lambda requests per month are free forever!


Master serverless computing through practice. The best way to learn Lambda is to build real applicationsβ€”start small, iterate, and scale as you grow more confident with event-driven architectures.