Level: Beginner to Intermediate
Table of Contents
- Course Overview
- Why Use AWS Lambda?
- Designing Event-Driven Architectures with AWS Lambda
- Lambda Concurrency Models
- AWS SAM — Serverless Application Model
- Advanced Code Snippets
- Advanced Serverless Architectures
- In-Depth Best Practices
- Summary and Key Takeaways
1. Course Overview
This course provides a gentle introduction to serverless cloud computing, focusing on the AWS Lambda service. This is not a programming course — the objective is to understand the core concepts and use cases of Lambda.
What you will learn:
- The key components of a Lambda function
- Creating simple Lambda functions
- Comparing Lambda and EC2
- Integrating Lambda into event-driven architectures
- Design patterns implementable with Lambda
This course forms the foundation for taking advanced courses in the AWS Lambda learning path.
2. Why Use AWS Lambda?
2.1 What is AWS Lambda?
Abstraction Levels in AWS
To understand Lambda, you need to grasp the different abstraction levels of cloud computing:
graph TD
A["🏢 Traditional Computing\n(On-premises)"] --> B["☁️ Cloud Computing - EC2\n(Virtual servers rented in AWS data centers)"]
B --> C["⚡ AWS Lambda\n(Serverless — servers are managed by AWS)"]
style A fill:#ff9999,stroke:#cc0000
style B fill:#99ccff,stroke:#0066cc
style C fill:#99ff99,stroke:#006600
| Level | Infrastructure Responsibility | Scaling Responsibility | Patching Responsibility |
|---|---|---|---|
| On-premises | You | You | You |
| EC2 | AWS (physical) | You | You |
| Lambda | AWS | AWS | AWS |
Official Definition
AWS Lambda is a compute service that lets you create applications without provisioning or managing servers. AWS provisions and manages the servers in the background — you only need to submit your code.
Core Characteristics of Lambda
| Characteristic | Detail |
|---|---|
| Execution | On-demand, triggered by an event |
| Billing | Per millisecond of compute time used |
| Supported Runtimes | Python, Node.js, Java, C#, Go, Ruby, and more |
| Maximum Duration | 15 minutes (900 seconds) per invocation |
| Memory | 128 MB to 10,240 MB (10 GB) |
| CPU | Proportional to allocated memory |
| Scaling | Automatic — up to 1,000 concurrent executions by default |
| Server Management | None (handled by AWS) |
| Deployment Package Size | 50 MB (zipped) / 250 MB (unzipped) |
| Ephemeral Storage /tmp | 512 MB to 10,240 MB |
How Lambda Works
sequenceDiagram
participant Dev as Developer
participant Lambda as AWS Lambda Service
participant AWS_Servers as AWS Servers (transparent)
Dev->>Lambda: Deploy code (deployment package)
Note over Lambda,AWS_Servers: AWS manages servers transparently
Dev->>Lambda: Configure a trigger (event source)
Note right of Dev: The event triggers the function
Lambda->>AWS_Servers: Initialize execution environment (Init phase)
AWS_Servers->>AWS_Servers: Execute the handler (Invoke phase)
AWS_Servers-->>Dev: Result (if synchronous invocation)
2.2 Lambda vs EC2 Comparison
EC2 — Overview
EC2 (Elastic Compute Cloud) is AWS’s flagship compute service. It provides virtual machines called EC2 instances.
EC2 Instance Selection:
- More than 752 instance types available
- 5 instance families:
mindmap
root((EC2 Families))
Compute Optimized
CPU-intensive workloads
Memory Optimized
In-memory databases
Storage Optimized
Big data, data warehouses
General Purpose
Web applications
Accelerated Computing
Machine Learning, GPU
Parameters to configure for EC2:
- Operating system
- Number of CPU cores
- RAM amount
- Storage space
- GPU cores
- Network bandwidth
- Regional availability
- Cost/benefit analysis
- Scaling strategy
- Storage type (persistent EBS vs ephemeral)
EC2 Cost Model: Billed per hour or second, even if the machine is idle. Designed for long-running operations (weeks to years).
Full Comparison Table
graph LR
subgraph EC2["🖥️ EC2"]
E1["✅ Full control of infrastructure"]
E2["✅ Persistent and long-term workloads"]
E3["✅ Migration of monolithic applications"]
E4["✅ Specific hardware configurations"]
E5["❌ Infrastructure management required"]
E6["❌ Billed even when idle"]
end
subgraph Lambda["⚡ Lambda"]
L1["✅ No servers to manage"]
L2["✅ Automatic scaling"]
L3["✅ Pay-per-use (per ms)"]
L4["✅ Ideal for developer teams"]
L5["❌ Max 15 minutes execution"]
L6["❌ Cold start possible"]
end
| Criterion | EC2 | Lambda |
|---|---|---|
| Server management | Developer | AWS |
| Execution duration | Unlimited | Max 15 minutes |
| Scaling | Manual / Auto Scaling Group | Automatic and immediate |
| Billing | Per hour/second (instance running) | Per millisecond (execution only) |
| Use case | Continuous workloads, monolithic migration | Event-driven tasks, microservices |
| Required expertise | Cloud architect | Developer |
| Startup time | Minutes | Milliseconds (warm) / a few seconds (cold) |
| OS control | Full | None |
| Memory persistence | Yes | No (between invocations) |
2.3 When to Choose AWS Lambda?
Recommended Use Cases for EC2
- Simple and straightforward application architectures
- Migration of monolithic applications from a local data center to the cloud
- Workloads requiring continuous and predictable execution
Recommended Use Cases for Lambda
- Completing a specific task in response to an event
- Team composed only of developers (no infrastructure expertise)
- Unpredictable or intermittent workloads
Concrete Lambda Use Cases
flowchart LR
subgraph UC1["📁 File Processing"]
direction TB
A1["High-res photo\nupload → S3"] --> B1["Lambda\nFunction"] --> C1["Low-res\nthumbnail → S3"]
end
subgraph UC2["🌐 Web/Mobile Applications"]
direction TB
A2["Internet Client"] --> B2["API Gateway"] --> C2["Lambda"] --> D2["DynamoDB"]
D2 --> C2 --> B2 --> A2
end
subgraph UC3["⏰ Automated Backups"]
direction TB
A3["EventBridge\nSchedule"] --> B3["Lambda\nFunction"] --> C3["Backup\nOperation"]
end
subgraph UC4["🔐 Auth & Compliance"]
direction TB
A4["Auth Request"] --> B4["Lambda\nValidation"] --> C4["Authorization\n/Denial"]
end
| Use Case | Description | Associated AWS Services |
|---|---|---|
| File processing | S3 upload triggers image resize | S3, Lambda |
| Web/mobile applications | Serverless backend with API | API Gateway, Lambda, DynamoDB |
| Automated backups | Near-real-time scheduled triggering | EventBridge, Lambda, S3 |
| Serverless websites | Hosting and backend logic | CloudFront, S3, Lambda |
| Compliance & Authentication | Near-real-time validation | Lambda, Cognito, IAM |
| Data analytics | Data transformation and routing | Lambda, Kinesis, Redshift |
Typical Serverless Architecture
flowchart LR
Internet["🌐 Internet\n(Multiple sources)"] --> APIGW["API Gateway\n(Ingestion)"]
APIGW --> Lambda1["Lambda\nFunction 1"]
APIGW --> Lambda2["Lambda\nFunction 2"]
Lambda1 --> DDB["DynamoDB"]
Lambda2 --> DDB
DDB --> Lambda1
DDB --> Lambda2
Lambda1 --> APIGW
Lambda2 --> APIGW
style APIGW fill:#ff9900,color:#000
style Lambda1 fill:#ff9900,color:#000
style Lambda2 fill:#ff9900,color:#000
style DDB fill:#3f51b5,color:#fff
3. Designing Event-Driven Architectures with AWS Lambda
3.1 Core Components of a Lambda Function
The Three Key Components
graph TD
Event["📨 Event\n(JSON Document)"] --> Handler
Context["🔧 Context Object\n(Invocation + environment info)"] --> Handler
subgraph LambdaFunction["⚡ Lambda Function"]
Handler["🚪 Lambda Handler\n(Entry point — method executed at invocation)"]
Handler --> Code["💻 Business code\n(Function logic)"]
end
Code --> Output["📤 Result / Actions\n(Other AWS services, response, etc.)"]
| Component | Role | Format |
|---|---|---|
| Handler | Entry point of the function, method called at invocation | Method/function in the code |
| Event object | Data passed to the function for processing | JSON document |
| Context object | Information about the invocation and execution environment | Object provided by Lambda runtime |
Example Lambda Function in Python (Hello World)
import json
# The handler is the entry point of the function
# 'event': JSON document containing input data
# 'context': information about the invocation and environment
def handler(event, context):
# The function can perform any processing operation
print("Received event: " + json.dumps(event, indent=2))
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
Example Lambda Function in Python with S3 Integration
import json
import boto3
import os
# ✅ Initialization outside handler (reused during warm executions)
s3_client = boto3.client('s3')
OUTPUT_BUCKET = os.environ['OUTPUT_BUCKET']
def handler(event, context):
"""
Processes an S3 upload and generates a copy in an output bucket.
Triggered by: s3:ObjectCreated event.
"""
for record in event['Records']:
source_bucket = record['s3']['bucket']['name']
object_key = record['s3']['object']['key']
print(f"Processing: s3://{source_bucket}/{object_key}")
# Copy the object to the output bucket
copy_source = {'Bucket': source_bucket, 'Key': object_key}
s3_client.copy_object(
CopySource=copy_source,
Bucket=OUTPUT_BUCKET,
Key=f"processed/{object_key}"
)
return {
'statusCode': 200,
'body': json.dumps(f'Processed {len(event["Records"])} records')
}
Example Lambda Function in Node.js (course demo)
'use strict';
exports.handler = (event, context, callback) => {
console.log('LogScheduledEvent');
// Log the received event as formatted JSON
console.log('Received event:', JSON.stringify(event, null, 2));
callback(null, 'Finished');
};
Note: Lambda supports multiple runtimes: Python, Node.js, Java, C#, Go, Ruby, and even custom runtimes via Lambda Custom Runtime.
Lambda Function Configuration — Key Attributes
When creating a function in the AWS Lambda console:
| Attribute | Options | Notes |
|---|---|---|
| Name | Unique string | Ex: LogScheduledEvent |
| Runtime | Python, Node.js, Java, etc. | Choose based on team skills |
| Handler | filename.method_name | Ex: lambda_function.handler |
| Memory | 128 MB to 10,240 MB | Also impacts allocated CPU |
| Timeout | 1s to 900s (15 min) | Adapt to workload |
| IAM Role | Execution role | Permissions to access other AWS services |
| Environment variables | Key/value pairs | Externalized configuration |
| VPC | Optional | Access to private resources |
Cold Start vs Warm Execution
sequenceDiagram
participant Event as Event Source
participant Lambda as Lambda Service
participant Container as Execution Environment
participant Code as Function Code
Note over Event,Code: 🥶 COLD START (first invocation or after idle)
Event->>Lambda: Invocation
Lambda->>Container: Container initialization
Container->>Code: Runtime initialization
Code->>Code: Code initialization (outside handler)
Code->>Code: Handler execution
Code-->>Event: Response (total latency: ~100ms to a few seconds)
Note over Event,Code: 🔥 WARM EXECUTION (subsequent invocations)
Event->>Lambda: Invocation
Lambda->>Container: Container already running
Container->>Code: Handler execution only
Code-->>Event: Response (a few milliseconds)
Practical implication: To optimize performance, place initialization code outside the handler. This code will only be executed once during the cold start, then reused during warm executions.
3.2 Event-Driven Architecture Overview
Definition
Event-driven architecture (EDA): A set of small, decoupled services that publish, consume, or route events. An event represents a state change or an update.
Event Examples
| Event | Context |
|---|---|
| Shopping cart filled | E-commerce site |
| Order ready to ship | Order management |
| Object uploaded to S3 | File storage |
| HTTP request received | REST API |
| Timer / schedule reached | Scheduled tasks |
The Three Components of an EDA
flowchart LR
subgraph Producers["📤 Event Producers"]
P1["Retail website"]
P2["Mobile application"]
P3["IoT Service"]
end
subgraph Router["🔀 Event Router"]
R1["Filters and routes\nevents"]
end
subgraph Consumers["📥 Event Consumers"]
C1["Service A"]
C2["Service B"]
C3["Service C"]
end
P1 --> R1
P2 --> R1
P3 --> R1
R1 --> C1
R1 --> C2
R1 --> C3
style Producers fill:#e3f2fd
style Router fill:#fff3e0
style Consumers fill:#e8f5e9
Key principle: The producer and consumer are decoupled. The producer doesn’t care about the consumer — it simply publishes the event. They can be updated and deployed independently.
EDA Benefits
| Benefit | Description |
|---|---|
| Scalability | Each component scales independently |
| Flexibility | Free chaining of components |
| Asynchronous communication | No temporal dependency on delivery/consumption |
| Fault tolerance | If one consumer goes down, others are unaffected |
| Extensibility | Easy to add new producers/consumers |
3.3 Lambda in Event-Driven Architectures
Lambda as an Event Router
flowchart TD
subgraph External["External Sources"]
E1["🪣 S3 Bucket\n(Object Upload)"]
E2["🌐 API Gateway\n(HTTP Request)"]
E3["⏰ EventBridge\n(Schedule)"]
end
subgraph LambdaService["⚡ AWS Lambda Service"]
API["Lambda API\n(Router)"]
F1["Function 1\n(Code package)"]
F2["Function 2\n(Code package)"]
F3["Function 3\n(Code package)"]
end
subgraph Downstream["Downstream Services"]
SNS["📧 SNS"]
SQS["📬 SQS"]
DDB["🗄️ DynamoDB"]
S3Out["🪣 S3 Output"]
end
E1 -->|"Event"| API
E2 -->|"Event"| API
E3 -->|"Event"| API
API --> F1
API --> F2
API --> F3
F1 --> SNS
F2 --> SQS
F3 --> DDB
F3 --> S3Out
style LambdaService fill:#fff3e0
style External fill:#e3f2fd
style Downstream fill:#e8f5e9
Important: The event does not interact directly with the Lambda function. It goes through the Lambda API which routes to the appropriate function. The function code is stored in a code deployment package.
Invocation Modes
graph TD
subgraph Trigger1["Trigger via S3"]
S3[S3 Bucket] -->|"ObjectCreated"| L1[Lambda Function]
L1 -->|"Resize image"| S3Out[S3 Output Bucket]
end
subgraph Trigger2["Trigger via API Gateway"]
Internet[Client] --> APIGW[API Gateway]
APIGW -->|"HTTP Request"| L2[Lambda Function]
L2 <-->|"Read/Write"| DB[DynamoDB]
L2 -->|"HTTP Response"| APIGW --> Internet
end
subgraph Trigger3["Scheduled trigger via EventBridge"]
EB[EventBridge Rule\nSchedule] -->|"Timer event"| L3[Lambda Function]
L3 -->|"Backup operation"| Storage[Storage]
end
| Invocation Mode | Event Source | Typical Use Case | Typical Duration |
|---|---|---|---|
| Synchronous | API Gateway, ALB, Lambda URL | REST APIs, web backends | Milliseconds to seconds |
| Asynchronous | S3, SNS, EventBridge | File processing, notifications | Milliseconds to minutes |
| Polling (Event Source Mapping) | SQS, DynamoDB Streams, Kinesis | Message processing, streams | Variable |
Max duration: A Lambda function can run for up to 15 minutes. In practice, most invocations last milliseconds to a few seconds.
3.4 Lambda Design Principles
Software Design Objectives
Lambda functions are software — the same good practices apply:
| Objective | Description |
|---|---|
| Reliability | The function must produce consistent results |
| Durability | Data must not be lost |
| Security | Minimum required access (principle of least privilege) |
| Performance | Optimized execution time |
| Cost efficiency | Billing per millisecond — optimize duration |
Design Patterns and Associated AWS Services
Tip: Don’t reinvent the wheel! AWS provides native services for each common pattern.
graph TD
subgraph Patterns["Design Patterns"]
P1["Queue Pattern"] --> S1["Amazon SQS"]
P2["Event Bus Pattern"] --> S2["Amazon EventBridge"]
P3["Publish/Subscribe Pattern"] --> S3["Amazon SNS"]
P4["Orchestration Pattern"] --> S4["AWS Step Functions"]
P5["API Pattern"] --> S5["Amazon API Gateway"]
P6["Event Streams Pattern"] --> S6["Amazon Kinesis"]
end
style P1 fill:#e3f2fd
style P2 fill:#e3f2fd
style P3 fill:#e3f2fd
style P4 fill:#e3f2fd
style P5 fill:#e3f2fd
style P6 fill:#e3f2fd
style S1 fill:#ff9900,color:#000
style S2 fill:#ff9900,color:#000
style S3 fill:#ff9900,color:#000
style S4 fill:#ff9900,color:#000
style S5 fill:#ff9900,color:#000
style S6 fill:#ff9900,color:#000
Lambda Function Design Best Practices
1. Short and focused functions
graph LR
subgraph Bad["❌ Bad practice"]
M["One large function\nthat does everything"]
end
subgraph Good["✅ Good practice"]
B1["Function A\n(single task)"]
B2["Function B\n(single task)"]
B3["Function C\n(single task)"]
end
style Bad fill:#ffebee
style Good fill:#e8f5e9
- Write multiple short functions rather than a few large ones
- Each function should accomplish one single task
- Result: concise functions with shorter execution times
- Important: max duration of 15 minutes — plan accordingly
2. Initialization optimization (warm execution)
import boto3
# ✅ Initialization code OUTSIDE the handler
# Executed once during cold start,
# reused during warm executions
dynamodb = boto3.resource('dynamodb')
user_table = dynamodb.Table('UserProfiles')
def handler(event, context):
# ✅ Concise handler, uses pre-initialized resources
response = user_table.get_item(Key={'id': event['userId']})
return response['Item']
3. Environment variables and secret management
- Use Lambda environment variables for configuration
- Never hardcode credentials in the code
- Use AWS Secrets Manager or AWS Systems Manager Parameter Store for secrets
4. Proper error handling
- Configure Dead Letter Queues (DLQ) for asynchronous invocations
- Use Lambda destinations for routing successes and errors
- Log with CloudWatch Logs for visibility
5. Lambda Layers
Lambda layers allow sharing code and dependencies between multiple functions:
graph TD
L1["Lambda Function A"] --> Layer["Lambda Layer\n(Shared dependencies\nlibraries, utilities)"]
L2["Lambda Function B"] --> Layer
L3["Lambda Function C"] --> Layer
style Layer fill:#ff9900,color:#000
6. Memory allocation
- Allocated memory also determines CPU power
- More memory = more CPU = faster execution
- Find the right balance between cost (memory × duration) and performance
- Use Lambda Power Tuning (open-source tool) to optimize
Best Practices Summary
| Practice | Reason |
|---|---|
| Short functions (one task) | Conciseness, reusability, ease of testing |
| Init code outside handler | Reuse during warm executions |
| Environment variables | Externalized config, no hardcoding |
| No credentials in code | Security — use IAM roles |
| Lambda layers | Share common dependencies |
| Appropriate memory | Cost/performance balance |
| Appropriate timeout | Avoid infinite executions |
| DLQ or destinations | Asynchronous error handling |
| Idempotent code | Handle duplicate events without side effects |
3.5 Demo: LogScheduledEvent with EventBridge and SNS
Demo Objective
Implement two design patterns in a single Lambda function:
- Event bus pattern (scheduled triggering via EventBridge)
- Publish/subscribe pattern (notification via SNS)
Demo Architecture
flowchart LR
EB["⏰ EventBridge Rule\n(Schedule: every 1 min)"]
Lambda["⚡ Lambda Function\nLogScheduledEvent\n(Node.js)"]
CW["📊 CloudWatch Logs\n(Log groups)"]
SNS["📢 SNS Topic\nsnsDemo"]
Email["📧 Email\n(DevOps Team)"]
EB -->|"Event JSON"| Lambda
Lambda -->|"Logs"| CW
Lambda -->|"Destination\n(on error)"| SNS
SNS -->|"Subscription\nProtocol: Email"| Email
style EB fill:#ff9900,color:#000
style Lambda fill:#ff9900,color:#000
style CW fill:#232f3e,color:#fff
style SNS fill:#ff9900,color:#000
Lambda Function Code (demos.js)
'use strict';
exports.handler = (event, context, callback) => {
console.log('LogScheduledEvent');
// Log the received event as formatted JSON
console.log('Received event:', JSON.stringify(event, null, 2));
callback(null, 'Finished');
};
Code explanation:
'use strict': JavaScript strict mode for better code qualityexports.handler: Defines the Lambda handler for Node.jsevent: The JSON object sent by EventBridge (contains schedule metadata)context: Information about the Lambda invocationcallback: Node.js callback function to signal end of executionJSON.stringify(event, null, 2): JSON formatting with indentation for log readability
Example JSON Event Sent by EventBridge
{
"version": "0",
"id": "12345678-1234-1234-1234-123456789012",
"detail-type": "Scheduled Event",
"source": "aws.events",
"account": "123456789012",
"time": "2024-01-15T10:00:00Z",
"region": "us-east-1",
"resources": [
"arn:aws:events:us-east-1:123456789012:rule/LogScheduledEvent"
],
"detail": {}
}
Configuration Steps
Step 1: Create the Lambda function
- In the AWS Lambda console → Create a function
- “Author from scratch”
- Runtime: Node.js
- Name:
LogScheduledEvent - Replace the code with the demo code
- Click Deploy
Step 2: Create the EventBridge rule
- Go to Amazon EventBridge
- Create a rule → EventBridge Scheduler
- Type: Recurring, rate-based
- Rate: every 1 minute
- Flexible time window: 5 minutes
- Target: Lambda function
LogScheduledEvent - Create the schedule
Step 3: Configure SNS for notifications
- Go to Amazon SNS → Topics
- Create (or select) a topic:
snsDemo - Create a subscription:
- Protocol: Email
- Endpoint: DevOps team email address
- Confirm the subscription via the received email
Step 4: Configure Lambda destination → SNS
- In the Lambda function → Configuration → Destinations
- Add a destination
- Type: SNS topic
- Select
snsDemo - Save
Step 5: Verification in CloudWatch
- Go to CloudWatch → Logs → Log groups
- Search for log group
/aws/lambda/LogScheduledEvent - Verify that log streams are created every minute
- Log events confirm that EventBridge is correctly triggering the function
Implemented Patterns
graph TD
subgraph Pattern1["Pattern 1: Event Bus"]
EB2["EventBridge\n(Event Bus)"] -->|"Schedule trigger"| Lambda2["Lambda Function"]
end
subgraph Pattern2["Pattern 2: Publish/Subscribe"]
Lambda3["Lambda Function"] -->|"Publish"| SNS2["SNS Topic\n(Broker)"]
SNS2 -->|"Subscribe"| Dev1["📧 Dev Email 1"]
SNS2 -->|"Subscribe"| Dev2["📧 Dev Email 2"]
SNS2 -->|"Subscribe"| Dev3["📱 SMS"]
end
style Pattern1 fill:#e3f2fd
style Pattern2 fill:#e8f5e9
Result: The DevOps team automatically receives an email whenever the Lambda function generates an error, without manually monitoring CloudWatch logs.
4. Lambda Concurrency Models
4.1 Concurrency and Automatic Scaling
Concurrency in Lambda represents the number of requests being processed simultaneously. For each concurrent request, Lambda provisions a separate execution environment.
sequenceDiagram
participant R1 as Request 1
participant R2 as Request 2
participant R3 as Request 3
participant LA as Lambda Service
participant E1 as Environment A
participant E2 as Environment B
participant E3 as Environment C
R1->>LA: Invocation
LA->>E1: New (Cold Start)
R2->>LA: Invocation (E1 busy)
LA->>E2: New (Cold Start)
R3->>LA: Invocation (E1 busy)
LA->>E3: New (Cold Start)
E1-->>R1: Response (E1 free)
R2->>LA: New request (E1 free)
LA->>E1: Reuse (Warm)
E2-->>R2: Response
E3-->>R3: Response
Concurrency calculation formula:
$$\text{Concurrency} = \text{Requests/second} \times \text{Average duration (seconds)}$$
Example:
- 100 requests/second with an average duration of 500ms → Concurrency = 50
- 100 requests/second with an average duration of 1s → Concurrency = 100
Default quotas:
- Concurrency limit per account: 1,000 simultaneous executions (per region)
- Scaling rate: 1,000 new environments every 10 seconds
- Requests per second: 10× the concurrency limit (i.e., 10,000 req/s by default)
4.2 Reserved Concurrency
Reserved concurrency guarantees an exclusive concurrency quota for a critical function, isolating it from the shared pool.
graph TD
subgraph Account["AWS Account — Total limit: 1,000"]
RC_Blue["Function Blue\nReserved: 400"]
RC_Orange["Function Orange\nReserved: 400"]
Unreserved["Other functions\nShared: 200"]
end
style RC_Blue fill:#1565c0,color:#fff
style RC_Orange fill:#e65100,color:#fff
style Unreserved fill:#e8f5e9
| Behavior | Reserved Concurrency |
|---|---|
| Guarantees X environments for the function | Yes |
| Pre-initializes environments | No |
| Eliminates cold starts | No |
| Additional cost | Free |
| Throttles other functions | Yes (consumes global pool) |
| Caps the function | Yes (cannot exceed the reservation) |
Use case: Protect a critical function from shared pool saturation, or intentionally limit a function’s scaling to protect downstream resources (e.g., database).
4.3 Provisioned Concurrency
Provisioned concurrency pre-initializes a fixed number of execution environments, eliminating cold starts for latency-sensitive functions.
sequenceDiagram
participant R as Request
participant LA as Lambda Service
participant PC as Pre-initialized Environment
Note over R,PC: 🚀 With Provisioned Concurrency
R->>LA: Invocation
LA->>PC: Environment already ready
PC-->>R: Immediate response (no cold start)
| Reserved Concurrency | Provisioned Concurrency | |
|---|---|---|
| Definition | Max number of reserved environments | Number of pre-initialized environments |
| Provisioning | On-demand | Before requests (pre-provisioned) |
| Cold starts | Possible | Eliminated (for provisioned units) |
| Cost | Free | Additional cost |
| Throttling | Yes, at reserved limit | Uses unreserved pool if exceeded |
When to use Provisioned Concurrency:
- APIs with critical low latency (payment, authentication)
- Applications sensitive to user experience
- Java functions with high cold starts (also use Lambda SnapStart for Java 11/17)
Configuration with SAM template:
MyFunction:
Type: AWS::Serverless::Function
Properties:
AutoPublishAlias: live
ProvisionedConcurrencyConfig:
ProvisionedConcurrentExecutions: 10
4.4 Calculating Concurrency
Practical example:
| Scenario | Req/s | Avg Duration | Concurrency | Required Action |
|---|---|---|---|---|
| Low load | 100 | 500ms | 50 | None |
| Medium load | 100 | 1s | 100 | Monitor |
| High load | 500 | 2s | 1000 | Request quota increase |
| Micro-duration | 20,000 | 50ms | 1000 | Watch req/s limit (10,000) |
Check quotas via AWS CLI:
aws lambda get-account-settings
Typical response:
{
"AccountLimit": {
"ConcurrentExecutions": 1000,
"UnreservedConcurrentExecutions": 900
}
}
5. AWS SAM — Serverless Application Model
5.1 What is SAM?
AWS SAM (Serverless Application Model) is an open-source framework for building serverless applications using Infrastructure as Code (IaC). It extends AWS CloudFormation with simplified syntax for defining serverless resources.
graph LR
SAM_Template["AWS SAM Template\n(template.yaml)"] -->|"sam build"| Build["Built artifact"]
Build -->|"sam deploy"| CF["CloudFormation Stack"]
CF --> Lambda["Lambda Functions"]
CF --> APIGW["API Gateway"]
CF --> DDB["DynamoDB Tables"]
CF --> Other["Other resources"]
style SAM_Template fill:#e3f2fd
style CF fill:#ff9900,color:#000
style Lambda fill:#ff9900,color:#000
Two main components:
- AWS SAM CLI — Command-line tool for developing, testing locally, and deploying
- AWS SAM Template — CloudFormation extension with simplified syntax
SAM Benefits:
- Defines a Lambda function, its API and DynamoDB table in ~20 lines (vs hundreds in pure CloudFormation)
- Local testing with
sam local invokeandsam local start-api - Built-in CI/CD with deployment pipeline
- Compatible with Terraform (via
sam local)
5.2 SAM Template: Structure and Syntax
A SAM template is a YAML file with the following sections:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31 # Tells CloudFormation to use SAM
Description: My serverless application
# Global variables shared between functions
Globals:
Function:
Timeout: 30
MemorySize: 256
Runtime: python3.12
Environment:
Variables:
LOG_LEVEL: INFO
# Configurable parameters at deployment
Parameters:
Environment:
Type: String
Default: dev
AllowedValues: [dev, staging, prod]
# Main resources
Resources:
# ...
# Exported outputs
Outputs:
# ...
5.3 SAM Template Examples
Complete Serverless CRUD Application
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Serverless CRUD API with Lambda and DynamoDB
Globals:
Function:
Timeout: 30
MemorySize: 256
Runtime: python3.12
Environment:
Variables:
TABLE_NAME: !Ref ItemsTable
Resources:
# Main Lambda function
GetItemFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: get-item
Handler: get_item.handler
CodeUri: src/
Description: Retrieves an item from DynamoDB
Policies:
- DynamoDBReadPolicy:
TableName: !Ref ItemsTable
Events:
GetItem:
Type: Api
Properties:
Path: /items/{id}
Method: get
RestApiId: !Ref ItemsApi
CreateItemFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: create-item
Handler: create_item.handler
CodeUri: src/
Description: Creates an item in DynamoDB
Policies:
- DynamoDBWritePolicy:
TableName: !Ref ItemsTable
Events:
CreateItem:
Type: Api
Properties:
Path: /items
Method: post
RestApiId: !Ref ItemsApi
# API Gateway
ItemsApi:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Environment
Cors:
AllowMethods: "'GET,POST,PUT,DELETE'"
AllowHeaders: "'Content-Type,Authorization'"
AllowOrigin: "'*'"
# DynamoDB Table
ItemsTable:
Type: AWS::Serverless::SimpleTable
Properties:
TableName: !Sub 'items-${Environment}'
PrimaryKey:
Name: id
Type: String
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
Outputs:
ApiUrl:
Description: API URL
Value: !Sub 'https://${ItemsApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}'
TableName:
Description: DynamoDB table name
Value: !Ref ItemsTable
Lambda with S3 Trigger and SNS Notification
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: S3 image processing with SNS notification
Resources:
# Image processing function
ImageProcessorFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: image-processor
Handler: processor.handler
Runtime: python3.12
MemorySize: 1024
Timeout: 300
Environment:
Variables:
OUTPUT_BUCKET: !Ref OutputBucket
SNS_TOPIC_ARN: !Ref NotificationTopic
Policies:
- S3ReadPolicy:
BucketName: !Ref InputBucket
- S3WritePolicy:
BucketName: !Ref OutputBucket
- SNSPublishMessagePolicy:
TopicName: !GetAtt NotificationTopic.TopicName
Events:
S3Upload:
Type: S3
Properties:
Bucket: !Ref InputBucket
Events: s3:ObjectCreated:*
Filter:
S3Key:
Rules:
- Name: suffix
Value: .jpg
# S3 Buckets
InputBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'images-input-${AWS::AccountId}'
OutputBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'images-output-${AWS::AccountId}'
# SNS Topic for notifications
NotificationTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: image-processing-notifications
# Email subscription
EmailSubscription:
Type: AWS::SNS::Subscription
Properties:
TopicArn: !Ref NotificationTopic
Protocol: email
Endpoint: devops@example.com
5.4 Essential SAM CLI Commands
# Initialize a new SAM project
sam init
# Build the project (resolve dependencies)
sam build
# Test a function locally
sam local invoke MyFunction --event events/test-event.json
# Start a local API for testing
sam local start-api
# Deploy (guided mode — recommended first time)
sam deploy --guided
# Deploy with existing parameters (CI/CD)
sam deploy
# Synchronize local changes to the cloud
sam sync --watch
# Delete the CloudFormation stack
sam delete
SAM configuration file (samconfig.toml):
version = 0.1
[default.deploy.parameters]
stack_name = "my-serverless-app"
s3_bucket = "my-artifacts-bucket"
region = "ca-central-1"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
parameter_overrides = "Environment=prod"
6. Advanced Code Snippets
6.1 Python: Common Patterns
Handler with DynamoDB and Error Handling
import json
import boto3
import os
import logging
from botocore.exceptions import ClientError
# Logger configuration at module level
logger = logging.getLogger()
logger.setLevel(os.environ.get('LOG_LEVEL', 'INFO'))
# Clients initialized outside handler (warm execution)
dynamodb = boto3.resource('dynamodb')
inventory_table = dynamodb.Table(os.environ['TABLE_NAME'])
def handler(event, context):
"""
Lambda handler for DynamoDB CRUD operations.
Triggered by API Gateway.
"""
http_method = event.get('httpMethod', 'GET')
path_params = event.get('pathParameters') or {}
body = json.loads(event.get('body') or '{}')
try:
if http_method == 'GET':
item_id = path_params.get('id')
response = inventory_table.get_item(Key={'id': item_id})
item = response.get('Item')
if not item:
return _response(404, {'error': 'Item not found'})
return _response(200, item)
elif http_method == 'POST':
import uuid
item = {**body, 'id': str(uuid.uuid4())}
inventory_table.put_item(Item=item)
return _response(201, item)
else:
return _response(405, {'error': 'Method not allowed'})
except ClientError as e:
logger.error(f"DynamoDB error: {e.response['Error']['Message']}")
return _response(500, {'error': 'Internal server error'})
def _response(status_code, body):
return {
'statusCode': status_code,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps(body, default=str)
}
Handler with SQS (Partial Batch Response)
import json
import logging
logger = logging.getLogger()
logger.setLevel('INFO')
def handler(event, context):
"""
Processes a batch of SQS messages.
Returns failed message IDs for retry (partial batch response).
"""
failed_messages = []
for record in event['Records']:
try:
message_body = json.loads(record['body'])
logger.info(f"Processing message: {record['messageId']}")
# Business logic here
process_message(message_body)
except Exception as e:
logger.error(f"Failed to process {record['messageId']}: {str(e)}")
failed_messages.append({'itemIdentifier': record['messageId']})
# Partial batch response — only retry failed messages
return {'batchItemFailures': failed_messages}
def process_message(message):
"""Business logic to process a message."""
logger.info(f"Processing: {message}")
# ... processing logic
6.2 Node.js: Common Patterns
Handler with AWS SDK v3 (async/await)
'use strict';
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');
// DynamoDB client initialized outside handler
const client = new DynamoDBClient({ region: process.env.AWS_REGION });
const TABLE_NAME = process.env.TABLE_NAME;
/**
* Lambda handler — Retrieves a DynamoDB item.
* Triggered by API Gateway (GET /items/{id})
*/
exports.handler = async (event, context) => {
const itemId = event.pathParameters?.id;
if (!itemId) {
return response(400, { error: 'Missing item ID' });
}
try {
const command = new GetItemCommand({
TableName: TABLE_NAME,
Key: marshall({ id: itemId })
});
const result = await client.send(command);
if (!result.Item) {
return response(404, { error: 'Item not found' });
}
return response(200, unmarshall(result.Item));
} catch (error) {
console.error('DynamoDB error:', error);
return response(500, { error: 'Internal server error' });
}
};
const response = (statusCode, body) => ({
statusCode,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
EventBridge Handler (Scheduled) with SNS — AWS SDK v3
'use strict';
const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');
// SNS client initialized outside handler
const snsClient = new SNSClient({ region: process.env.AWS_REGION });
const SNS_TOPIC_ARN = process.env.SNS_TOPIC_ARN;
/**
* Lambda handler — Logs a scheduled event and publishes to SNS.
* Reproduces the course demo with AWS SDK v3.
*/
exports.handler = async (event, context) => {
console.log('LogScheduledEvent');
console.log('Received event:', JSON.stringify(event, null, 2));
try {
const command = new PublishCommand({
TopicArn: SNS_TOPIC_ARN,
Subject: 'Lambda Scheduled Event Executed',
Message: JSON.stringify({
timestamp: new Date().toISOString(),
functionName: context.functionName,
requestId: context.awsRequestId,
event: event
}, null, 2)
});
await snsClient.send(command);
console.log('SNS notification sent successfully');
} catch (error) {
console.error('Failed to send SNS notification:', error);
throw error; // Rethrow to trigger the error destination
}
return { statusCode: 200, body: 'Finished' };
};
7. Advanced Serverless Architectures
7.1 Serverless vs Traditional: Comparison Table
graph LR
subgraph Traditional["🏢 Traditional Architecture"]
T1["Servers always running"]
T2["Manual scaling or ASG"]
T3["Fixed cost (idle servers)"]
T4["OS and patching management"]
T5["High availability to configure"]
end
subgraph Serverless["⚡ Serverless Architecture"]
S1["On-demand compute"]
S2["Instant automatic scaling"]
S3["Pay-per-use (ms)"]
S4["No OS management"]
S5["Native AWS HA"]
end
| Dimension | Traditional (EC2) | Serverless (Lambda) |
|---|---|---|
| Infrastructure management | DevOps team required | None |
| Scaling | Minutes (ASG) | Seconds (automatic) |
| Idle cost | Continuous billing | Zero |
| Cold start | None (always warm) | A few milliseconds to seconds |
| Max duration | Unlimited | 15 minutes |
| Stateful | Yes (persistent memory) | No (stateless) |
| CI/CD | Deployment on VM | Code deployment only |
| Debugging | SSH, OS logs | CloudWatch, X-Ray |
| Local testing | VM environment | SAM local, Docker |
| Vendor lock-in | Low (VM portability) | Medium (AWS-specific) |
7.2 Lambda vs Containers
Lambda supports running container images (up to 10 GB via Amazon ECR). Here’s when to prefer one over the other:
graph TD
subgraph Lambda_Zip["Lambda — Deployment Package ZIP"]
Z1["Fast deployment time"]
Z2["Limited size: 250MB"]
Z3["Runtimes managed by AWS"]
Z4["Ideal: simple business logic"]
end
subgraph Lambda_Container["Lambda — Container Image"]
C1["Size up to 10 GB"]
C2["Complex dependencies (ML libs)"]
C3["Full reproducibility"]
C4["Ideal: ML inference, media processing"]
end
subgraph ECS_Fargate["ECS/Fargate — Containers"]
F1["Long-running execution"]
F2["Full container control"]
F3["HTTP polling (not event-driven)"]
F4["Ideal: APIs, persistent microservices"]
end
| Criterion | Lambda (ZIP) | Lambda (Container) | ECS/Fargate |
|---|---|---|---|
| Max size | 250 MB | 10 GB | Unlimited |
| Max duration | 15 min | 15 min | Unlimited |
| Cold start | Low | Higher | N/A (persistent) |
| Infra management | None | None | Minimal |
| Use case | Business logic | ML, heavy deps | Persistent services |
7.3 Serverless Architecture Patterns
Pattern 1: Serverless REST API
flowchart LR
Client["📱 Client\n(Mobile/Web)"] --> APIGW["API Gateway\n(REST)"]
APIGW --> Auth["Lambda\nAuthorizer"]
Auth -->|"Authorized"| Handler["Lambda\nFunction"]
Handler --> DDB["DynamoDB"]
Handler --> Cache["ElastiCache\n(Redis)"]
style APIGW fill:#ff9900,color:#000
style Handler fill:#ff9900,color:#000
style Auth fill:#ff9900,color:#000
Pattern 2: Data Processing Pipeline
flowchart LR
S3["S3\n(Upload CSV)"] -->|"Event"| Lambda1["Lambda\nIngestion"]
Lambda1 --> SQS["SQS Queue"]
SQS -->|"Batch"| Lambda2["Lambda\nTransformation"]
Lambda2 --> DDB["DynamoDB\n(Results)"]
Lambda2 -->|"Error"| DLQ["SQS Dead\nLetter Queue"]
DLQ --> Lambda3["Lambda\nError Handler"]
Lambda3 --> SNS["SNS\n(Alert)"]
style Lambda1 fill:#ff9900,color:#000
style Lambda2 fill:#ff9900,color:#000
style Lambda3 fill:#ff9900,color:#000
Pattern 3: Orchestration with Step Functions
flowchart TD
Start(["▶ Start"]) --> ValidateInput["Lambda\nValidate Input"]
ValidateInput -->|"Valid"| ProcessOrder["Lambda\nProcess Order"]
ValidateInput -->|"Invalid"| Reject["Lambda\nReject"]
ProcessOrder --> CheckInventory["Lambda\nCheck Inventory"]
CheckInventory -->|"In stock"| ChargePayment["Lambda\nCharge Payment"]
CheckInventory -->|"Out of stock"| Backorder["Lambda\nBackorder"]
ChargePayment -->|"Success"| ShipOrder["Lambda\nShip Order"]
ChargePayment -->|"Failure"| RefundProcess["Lambda\nRefund"]
ShipOrder --> NotifyCustomer["Lambda\nNotify Customer"]
NotifyCustomer --> End(["⏹ End"])
style ValidateInput fill:#ff9900,color:#000
style ProcessOrder fill:#ff9900,color:#000
style CheckInventory fill:#ff9900,color:#000
style ChargePayment fill:#ff9900,color:#000
style ShipOrder fill:#ff9900,color:#000
style NotifyCustomer fill:#ff9900,color:#000
Step Functions allows orchestrating complex multi-Lambda workflows with state management, retries and conditional branches, without writing coordination code.
Pattern 4: Fan-out Architecture with SNS + SQS
flowchart TD
Producer["Lambda\nProducer"] --> SNS["SNS Topic\n(Fan-out)"]
SNS --> SQS1["SQS Queue\nService A"]
SNS --> SQS2["SQS Queue\nService B"]
SNS --> SQS3["SQS Queue\nService C"]
SQS1 --> LambdaA["Lambda\nService A"]
SQS2 --> LambdaB["Lambda\nService B"]
SQS3 --> LambdaC["Lambda\nService C"]
style Producer fill:#ff9900,color:#000
style LambdaA fill:#ff9900,color:#000
style LambdaB fill:#ff9900,color:#000
style LambdaC fill:#ff9900,color:#000
This pattern combines SNS (fan-out) and SQS (buffer/retry) to maximize resilience and decoupling. Each consuming service can process messages at its own pace.
8. In-Depth Best Practices
Code
| Practice | Detail |
|---|---|
| Initialization outside handler | SDK clients, DB connections, static files in /tmp |
| Keep-alive connections | Use keep-alive for persistent HTTP connections |
| No self-invocation | Avoid recursive calls — risk of cost escalation |
| No internal AWS APIs | Use only documented public APIs |
| Idempotent code | Events can be delivered multiple times (at-least-once) |
| No user data in global memory | Risk of data leakage between different invocations |
Configuration
| Practice | Detail |
|---|---|
| Performance testing | Use Lambda Power Tuning to optimize memory/cost |
| Load testing | Determine an appropriate timeout for the real workload |
| IAM least privilege | Minimum permissions in the execution role |
| Delete unused functions | Avoids consuming deployment package size quota |
| SQS timeout = Lambda timeout | SQS visibility timeout must be greater than or equal to Lambda timeout |
Scalability
| Practice | Detail |
|---|---|
| Know upstream/downstream limits | RDS, DynamoDB, third-party APIs can become bottlenecks |
| Reserved Concurrency to control scaling | Protect downstream resources |
| Provisioned Concurrency for latency | Critical functions with strict SLAs |
| Retries with jitter and exponential backoff | Avoid retry storms |
Observability
| Practice | Detail |
|---|---|
| CloudWatch Metrics + Alarms | Don’t implement custom metrics from code |
| Embedded Metric Format (EMF) | Emit metrics via logs (asynchronous, more performant) |
| Structured JSON logging | Facilitates search and analysis in CloudWatch Insights |
| AWS X-Ray tracing | Trace distributed requests across services |
| GuardDuty Lambda Protection | Detect suspicious network activities |
Security
graph TD
subgraph Security["🔐 Lambda Security — Best Practices"]
S1["IAM Role\n(Least privilege)"]
S2["Environment variables\n(No secrets in plain text)"]
S3["Secrets Manager\nor Parameter Store"]
S4["VPC if needed\n(private resource access)"]
S5["Security Hub\n(CSPM compliance)"]
S6["GuardDuty\n(Threat detection)"]
end
9. Summary and Key Takeaways
Final Comparison of AWS Compute Services
graph LR
subgraph Continuum["Management Spectrum"]
EC2["EC2\nMaximum control\nMaximum management"] --- EB2["Elastic Beanstalk\nIntermediate management"] --- Lambda["Lambda\nNo servers\nMinimum management"]
end
style EC2 fill:#ff9999
style EB2 fill:#ffcc99
style Lambda fill:#99ff99
Key Points to Remember
| Concept | Key Points |
|---|---|
| Lambda = Serverless | Servers exist but are managed by AWS |
| Event-driven | Lambda is triggered by events, not by continuous execution |
| Pay-per-use | Billing only for actual execution time (per ms) |
| 15 min max | Maximum timeout per invocation |
| Cold start | Initial delay during the first invocation or after an idle period |
| One task per function | Design best practice |
| Don’t reinvent the wheel | Use native AWS services (SQS, SNS, EventBridge, etc.) |
| Reserved Concurrency | Protects and isolates critical functions (free) |
| Provisioned Concurrency | Eliminates cold starts (paid) |
| SAM | Simplified IaC framework for serverless applications |
AWS Services Complementary to Lambda
| Service | Role with Lambda |
|---|---|
| API Gateway | HTTP/REST trigger, API exposure |
| S3 | Trigger on file upload |
| EventBridge | Scheduled triggers and event bus |
| SQS | Message queue, decoupling, automatic retry |
| SNS | Push notifications, publish/subscribe, fan-out |
| DynamoDB | Serverless NoSQL database |
| CloudWatch | Monitoring, logs, function metrics |
| X-Ray | Distributed tracing for debugging |
| Step Functions | Lambda workflow orchestration |
| Kinesis | Real-time event streams |
| SAM | Infrastructure as Code for serverless |
| Secrets Manager | Secure secret management |
| Cognito | Authentication and authorization |
| ECR | Registry for Lambda container images |
Decision Architecture: Lambda or Not?
flowchart TD
Q1{"Execution duration\n> 15 minutes?"}
Q1 -->|"Yes"| EC2_ECS["EC2 / ECS Fargate\nor Step Functions"]
Q1 -->|"No"| Q2{"Continuous workload\n(24/7)?"}
Q2 -->|"Yes"| EC2["EC2 or ECS"]
Q2 -->|"No"| Q3{"Triggered by\nan event?"}
Q3 -->|"Yes"| Lambda["✅ AWS Lambda\n(Recommended)"]
Q3 -->|"No"| Q4{"Unpredictable\nworkload?"}
Q4 -->|"Yes"| Lambda
Q4 -->|"No"| Both["Lambda or EC2\nbased on team expertise"]
style Lambda fill:#99ff99,stroke:#006600
style EC2 fill:#ff9999
style EC2_ECS fill:#ffcc99
Next Steps in the Learning Path
This course forms the foundation for:
- Advanced courses on Lambda configuration (VPC, layers, versions, aliases)
- Lambda security (IAM roles, resource policies)
- Performance optimization (Power Tuning, Provisioned Concurrency)
- Lambda with containers (Docker images)
- CI/CD for Lambda (SAM, CDK, Serverless Framework)
- Advanced monitoring (X-Ray tracing, CloudWatch Insights)
- Orchestration with AWS Step Functions
- Lambda@Edge for CDN workloads
Search Terms
serverless · aws · lambda · amazon · web · services · function · architecture · concurrency · patterns · sam · comparison · design · handler · pattern · sns · architectures · cases · components · configuration · ec2 · event · event-driven · eventbridge