Build Serverless Functions
Create and deploy serverless functions with AWS Lambda, Google Cloud Functions, and Azure Functions for event-driven, pay-per-use compute.
Overview
Serverless computing lets you run code without provisioning or managing servers. You write functions, upload them to a cloud provider, and the platform handles scaling, patching, and availability automatically. You pay only for execution time — making it ideal for sporadic workloads and event-driven architectures.
Below is a practical approach to creating and deploying serverless functions with AWS Lambda, Google Cloud Functions, and Azure Functions.
When to Use
Use this resource when:
- You have event-driven workloads (webhooks, file processing, scheduled jobs). See Event-Driven Functions for event-driven patterns.
- You want automatic scaling from zero to thousands of requests. See Cold Start Optimization for minimizing startup latency.
- You need to avoid server maintenance and infrastructure overhead
- Your traffic is sporadic and provisioning servers would be wasteful. See Serverless API Gateway for building pay-per-use APIs.
Solution
Python (AWS Lambda)
import json
import boto3
def handler(event, context):
# Extract query parameter
name = event.get("queryStringParameters", {}).get("name", "World")
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": f"Hello, {name}!"})
}
JavaScript (Google Cloud Functions)
const functions = require('@google-cloud/functions-framework');
functions.http('helloHttp', (req, res) => {
const name = req.query.name || 'World';
res.json({ message: `Hello, ${name}!` });
});
Java (Azure Functions)
import com.microsoft.azure.functions.*;
import java.util.Optional;
public class HelloFunction {
@FunctionName("hello")
public HttpResponseMessage run(
@HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
String name = request.getQueryParameters().getOrDefault("name", "World");
return request.createResponseBuilder(HttpStatus.OK)
.body("Hello, " + name + "!")
.build();
}
}
Explanation
Serverless platforms abstract infrastructure management:
- Event triggers: HTTP requests, file uploads, database changes, timers
- Automatic scaling: Each invocation runs in a fresh container; platforms scale containers up and down
- Pay-per-use: Billed by milliseconds of execution and number of invocations
Cold starts occur when a function hasn’t been invoked recently. The platform must initialize a new container, adding latency (100ms–3s depending on runtime and memory allocation).
Variants
| Platform | Runtime | Trigger Types | Cold Start |
|---|---|---|---|
| AWS Lambda | Python, Node, Java, Go, Ruby | HTTP, S3, SNS, SQS, EventBridge, Cron | 100ms–1s |
| Cloud Functions | Node, Python, Go, Java | HTTP, Pub/Sub, Storage, Firestore, Cron | 200ms–2s |
| Azure Functions | Node, Python, Java, C# | HTTP, Blob, Queue, Event Grid, Timer | 200ms–3s |
What Works
- Keep functions small and focused: One function per responsibility; compose complex workflows with step functions
- Minimize deployment package size: Remove unnecessary dependencies to reduce cold start time
- Use provisioned concurrency for latency-sensitive workloads: Pre-warm containers for critical paths
- Store state externally: Functions are stateless; use DynamoDB, Redis, or Cloud Firestore for persistence
- Set memory and timeout limits appropriately: Undersized memory causes OOM; oversized wastes money
Common Mistakes
- Storing state in the function container: Local variables are lost between invocations; containers may be reused, but never depend on it
- Ignoring cold starts: User-facing synchronous APIs suffer from cold start latency
- Over-provisioning memory: Lambda allocates CPU proportionally to memory; find the sweet spot
- Not handling partial failures: Batch processing must handle retries without duplicating work
- Tight coupling to one vendor: Use abstraction layers (Serverless Framework, SAM) for portability
Advanced: Cold Start Mitigation
# AWS Lambda: initialize outside handler for connection reuse
import json
import boto3
# These run on cold start only — reused across invocations
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('users')
http_session = requests.Session()
def handler(event, context):
# Handler stays thin — connections are already warm
user_id = event['pathParameters']['id']
response = table.get_item(Key={'id': user_id})
return {
'statusCode': 200,
'body': json.dumps(response.get('Item', {}))
}
Initialize database connections, HTTP clients, and heavy imports outside the handler function. The platform reuses the container across invocations, so these initializations run once on cold start and persist. Keep the handler body minimal — just process the event and return.
Advanced: Step Functions Orchestration
{
"StartAt": "ValidateOrder",
"States": {
"ValidateOrder": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123:function:ValidateOrder",
"Next": "CheckInventory"
},
"CheckInventory": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123:function:CheckInventory",
"Next": "ProcessPayment"
},
"ProcessPayment": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123:function:ProcessPayment",
"End": true
}
}
}
Step Functions chain Lambda invocations with built-in retries, error handling, and state management. Each step is independently retryable. Use Choice states for conditional branching, Parallel states for concurrent execution, and Map states for fan-out patterns.
Advanced: Provisioned Concurrency
# AWS CLI: configure provisioned concurrency
aws lambda put-provisioned-concurrency-config \
--function-name my-api \
--qualifier production \
--provisioned-concurrent-executions 10
Provisioned concurrency keeps execution environments warm and ready to respond. Configure it for latency-sensitive endpoints where cold starts are unacceptable. Monitor utilization — if provisioned capacity sits idle, reduce it. For spiky traffic, combine provisioned concurrency with on-demand scaling.
Advanced: Event-Driven Architecture
import json
import boto3
s3 = boto3.client('s3')
sns = boto3.client('sns')
def handler(event, context):
# Triggered by S3 upload
for record in event['Records']:
bucket = record['s3']['bucket']['name']
key = record['s3']['object']['key']
# Process uploaded file
response = s3.get_object(Bucket=bucket, Key=key)
content = response['Body'].read().decode('utf-8')
# Publish result to SNS
sns.publish(
TopicArn='arn:aws:sns:us-east-1:123:processing-complete',
Message=json.dumps({
'file': key,
'size': len(content),
'lines': content.count('\n')
})
)
return {'statusCode': 200, 'body': json.dumps({'processed': len(event['Records'])})}
Event-driven functions respond to cloud events: S3 uploads, DynamoDB streams, SQS messages, SNS notifications, or EventBridge schedules. Each event contains records that your handler processes independently. Design handlers to be idempotent because event sources may deliver duplicates. Use partial batch responses (Lambda) to report which records succeeded and which should be retried.
Advanced: Local Development
# template.yaml — AWS SAM local development
Resources:
ProcessUpload:
Type: AWS::Serverless::Function
Properties:
Handler: app.handler
Runtime: python3.11
Events:
Upload:
Type: S3
Properties:
Bucket: uploads
Events: s3:ObjectCreated:*
# Run locally:
# sam local start-api
# sam local invoke ProcessUpload --event events/s3-upload.json
# sam local generate-event s3 --bucket uploads --key data.csv > events/s3-upload.json
AWS SAM CLI, Azure Functions Core Tools, and Google Cloud Functions Emulator provide local execution environments. Generate sample events with sam local generate-event to test handler logic without deploying. Use sam local start-api to test HTTP-triggered functions with curl or Postman. Always run a final integration test in the cloud — local emulators don’t replicate all platform behaviors.
When to Avoid
- Long-running computations: Tasks exceeding 15 minutes (Lambda) need container-based compute (ECS, Cloud Run, EKS)
- Stateful applications: Real-time games, WebSocket servers, or streaming services need persistent connections
- High-frequency invocations: If your function runs 1000+ times per second, a always-on server may be cheaper
- Complex dependencies: Large native libraries or custom runtimes increase cold start and deployment complexity
Troubleshooting
- Cold start latency is high: increase provisioned concurrency, reduce package size, and avoid initializing heavy clients per invocation.
- Function times out: check downstream dependencies, memory allocation, and retry logic. Increase timeout only after optimizing the code.
- State lost between invocations: serverless functions are stateless. Persist state in a database, cache, or durable queue.
- Deployment package too large: exclude dev dependencies and unused assets.
- Event ordering issues: many event sources are at-least-once and unordered. Design for idempotency and explicit sequencing.
Further Reading
- Official documentation: check the current reference for the framework or tool used.
- Related guides: explore the serverless and lambda guides for deeper coverage.
- Complementary patterns: review design patterns applicable to your technology stack.
- Public postmortems: study real incidents from teams that faced similar production issues.
Production Notes
- Deploy gradually using canary or blue-green to catch regressions early.
- Configure alerts for error rate, p99 latency, and failure rate before enabling in production.
- Document the rollback in the runbook; test the procedure in staging at least once per quarter.
- Review structured logs with correlation IDs to trace requests end-to-end during incidents.
Key Takeaways
- Apply build serverless functions when you need a practical solution for your use case.
- Monitor performance after implementation; measure latency, errors, and resource usage before and after.
- Check the Troubleshooting section for common failures; most have documented root causes with fixes.
- Keep dependencies updated and run tests in CI to prevent production regressions.
Common Production Pitfalls
- Copying the example without adapting it to real data volumes and failure modes.
- Skipping load and error-injection tests before the first production deployment.
- Hard-coding values that should be configurable per environment.
- Forgetting to add logging and monitoring at each step.
- Deploying without a rollback plan or a tested backup strategy.
- Assuming the minimal example will scale without adding caching or batching.
- Not documenting the version and configuration used in production.
- Letting the recipe sit unchanged when dependencies or scale evolve.
Frequently Asked Questions
How do I reduce cold start latency?
Use smaller runtimes (Node.js, Python) over Java. Reduce package size. Use provisioned concurrency. Keep connections (database, HTTP) warm between invocations by initializing outside the handler.
Can serverless functions handle long-running tasks?
AWS Lambda max is 15 minutes, Cloud Functions 60 minutes, Azure Functions configurable. For longer tasks, use step functions to orchestrate multiple short functions or move to container-based compute (ECS, Cloud Run).
How do I debug serverless functions locally?
AWS SAM CLI, Azure Functions Core Tools, and Functions Framework for Node.js all provide local emulators. Test locally, but always validate in the cloud since behavior can differ.
Related Resources
Event-Driven Architecture — Queues, Topics, and Streams
A practical guide to event-driven architecture: events vs commands, message brokers, patterns like CQRS and Saga, and when to choose async over sync.
GuideSoftware Architecture Guide
A guide to designing software architecture: monoliths vs microservices, layered architecture, data flow, and technology selection criteria.
PatternObserver Pattern
Define a subscription mechanism to notify multiple objects about events. A behavioral design pattern for event-driven communication.
RecipeImplement Event Sourcing in Serverless Architectures
How to capture all changes as immutable events using event sourcing with AWS Lambda, DynamoDB streams, and event stores for audit trails and temporal queries.
RecipeMinimize Cold Start Latency in Serverless Functions
How to reduce cold start times in AWS Lambda, Azure Functions, and Cloud Run using provisioned concurrency, lazy loading, runtime tuning, and dependency optimization.