AWS Lambda Explained: A Beginner-Friendly Introduction

By Oleksandr Andrushchenko — Published on — Modified on

AWS Lambda Explained: A Beginner-Friendly Introduction
AWS Lambda Explained: A Beginner-Friendly Introduction

AWS Lambda is a serverless compute service that allows you to run code without creating or managing servers. Instead of provisioning EC2 instances, installing runtimes, configuring scaling rules, and patching operating systems, you write a function and configure when it should run.

Lambda is commonly used for REST APIs, background jobs, file processing, scheduled tasks, queue workers, and event-driven systems. This article explains the core ideas with practical examples, code snippets, tables, and simple architecture diagrams.

Table of Contents

What Is AWS Lambda?

AWS Lambda is a managed compute service where the main deployment unit is a function. You upload code, configure a trigger, and AWS runs that code when the trigger fires.

Event -> Lambda Function -> Your Code -> Result

A Lambda function can handle an HTTP request, process an uploaded file, consume queue messages, react to database changes, or run on a schedule.

What Does Serverless Mean?

Serverless does not mean there are no servers. It means you do not manage those servers directly. AWS handles the infrastructure, runtime, scaling, availability, and much of the operational work.

  • You write code.
  • AWS runs the code.
  • AWS scales the infrastructure.
  • You pay based on usage.

Function as a Service

Lambda is often described as Function as a Service, or FaaS. Instead of deploying a full server application, you deploy focused functions that run when needed.

def lambda_handler(event, context):
    return "Hello from Lambda"

Lambda vs Traditional Server

Traditional Server AWS Lambda
Application runs continuously Function runs when invoked
You manage server capacity AWS scales automatically
You patch and maintain the OS AWS manages the infrastructure
You pay for uptime You pay mostly for requests and duration
Good for long-running services Good for short event-driven tasks

How AWS Lambda Works

Lambda is built around events. An event happens, Lambda receives input, your handler function runs, and the function either returns a response or performs a side effect.

Basic Lambda Flow

  1. An event happens in AWS or an external system.
  2. A trigger invokes the Lambda function.
  3. Lambda prepares an execution environment.
  4. Your handler receives the event.
  5. Your code processes the event.
  6. The function returns a result or writes data somewhere.
Trigger -> Event Object -> Handler Function -> Database / Queue / Response / Logs

The Event Object

The event object contains data from the service that triggered the function. Its structure depends on the trigger.

{
  "httpMethod": "GET",
  "path": "/users/42",
  "queryStringParameters": {
    "includeOrders": "true"
  }
}
def lambda_handler(event, context):
    path = event["path"]

    return {
        "statusCode": 200,
        "body": f"Requested path: {path}"
    }

The Context Object

The context object contains runtime information about the function invocation, such as request ID, function name, memory limit, and remaining execution time.

Object Purpose
event Input data from API Gateway, S3, SQS, EventBridge, or another trigger
context Runtime metadata about the current Lambda invocation
return Output returned to the caller or integration

Writing Your First Lambda Function

A basic Lambda function usually has a handler. The handler is the entry point AWS calls when your function is invoked.

Python Example

import json

def lambda_handler(event, context):
    name = event.get("name", "developer")

    response = {
        "message": f"Hello, {name}!"
    }

    return {
        "statusCode": 200,
        "body": json.dumps(response)
    }

Example input:

{
  "name": "Alex"
}

Example output:

{
  "statusCode": 200,
  "body": "{\"message\": \"Hello, Alex!\"}"
}

Returning an API Response

When Lambda is used behind API Gateway, it often returns an object with statusCode, headers, and body.

import json

def lambda_handler(event, context):
    user = {
        "id": 42,
        "name": "Alex"
    }

    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps(user)
    }

Using Environment Variables

Environment variables are useful for configuration values such as table names, bucket names, feature flags, and API endpoints.

import os

TABLE_NAME = os.environ["TABLE_NAME"]

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": f"Using table: {TABLE_NAME}"
    }

Rule of thumb: do not hardcode environment-specific values directly in the function code.

Common AWS Lambda Triggers

Lambda functions usually run because another service invokes them. This is what makes Lambda useful for event-driven architecture.

Trigger Use Case Example
API Gateway HTTP APIs GET /users/42 invokes Lambda
S3 File processing Upload image, create thumbnail
SQS Queue workers Process background jobs
SNS Pub/sub notifications Notify multiple subscribers
EventBridge Event routing and schedules Run scheduled cleanup
DynamoDB Streams React to database changes Process newly inserted records

API Gateway

API Gateway can route HTTP requests to Lambda. This is a common way to build serverless REST APIs.

Client -> API Gateway -> Lambda -> DynamoDB -> Response
import json

def lambda_handler(event, context):
    user_id = event["pathParameters"]["userId"]

    user = {
        "id": user_id,
        "name": "Demo User"
    }

    return {
        "statusCode": 200,
        "body": json.dumps(user)
    }

S3 Events

Amazon S3 can invoke Lambda when a file is uploaded, deleted, or changed. This is useful for image processing, file validation, metadata extraction, and import pipelines.

Upload File -> S3 Event -> Lambda -> Process File
def lambda_handler(event, context):
    for record in event["Records"]:
        bucket = record["s3"]["bucket"]["name"]
        key = record["s3"]["object"]["key"]

        print(f"New file uploaded: s3://{bucket}/{key}")

    return {
        "processed": len(event["Records"])
    }

SQS and SNS

Amazon SQS is commonly used for queue-based background jobs. Amazon SNS is commonly used for pub/sub notifications.

def lambda_handler(event, context):
    for record in event["Records"]:
        message = record["body"]
        print(f"Processing message: {message}")

    return {
        "processed": len(event["Records"])
    }
Service Best For
SQS Reliable queue processing and background workers
SNS Fan-out notifications to multiple subscribers

EventBridge

EventBridge can route events from AWS services, custom applications, schedules, and SaaS integrations to Lambda functions.

Application Event -> EventBridge -> Lambda Consumer

EventBridge is useful when you want to decouple systems. One service publishes an event, and another service reacts to it without direct dependency between them.

DynamoDB Streams

DynamoDB Streams can invoke Lambda when records in a DynamoDB table are inserted, updated, or deleted.

  • Send notification after a new record is created.
  • Update a search index after data changes.
  • Write audit logs.
  • Synchronize data into another system.

Lambda Execution Lifecycle

Lambda functions are not always started from zero. AWS can create an execution environment and reuse it for future invocations.

Cold Starts

A cold start happens when Lambda needs to create a new execution environment before running your function. This can add latency because AWS needs to prepare the runtime and initialize your code.

Warm Starts

A warm start happens when Lambda reuses an existing execution environment. Warm starts are usually faster because some initialization work has already happened.

Type What Happens Typical Result
Cold start New environment is created Slower invocation
Warm start Existing environment is reused Faster invocation
Init Phase -> Invoke Phase -> Freeze -> Possible Reuse -> Next Invoke

Global Variable Reuse

Code outside the handler runs during initialization and may be reused during warm invocations. This is useful for SDK clients, configuration loading, and expensive setup.

import boto3

s3_client = boto3.client("s3")

def lambda_handler(event, context):
    buckets = s3_client.list_buckets()

    return {
        "bucketCount": len(buckets["Buckets"])
    }

Important: reuse global clients, but do not store request-specific user state in global variables.

Scaling and Concurrency

Concurrency means the number of Lambda invocations running at the same time. If 100 requests are being processed at the same moment, the function has about 100 concurrent executions.

100 Incoming Requests -> 100 Parallel Lambda Executions
Feature Purpose
Default concurrency Lambda scales automatically within account and regional quotas.
Reserved concurrency Reserves capacity for one function and can also limit its maximum concurrency.
Provisioned concurrency Keeps environments initialized to reduce cold start latency.

Important: Lambda can scale quickly, but downstream systems may not. A database, external API, or legacy service can become the bottleneck.

Bad design:
1,000 Lambda executions -> 1,000 direct database connections

Better design:
1,000 Lambda executions -> SQS / RDS Proxy / throttling / connection reuse

AWS Lambda Pricing

Lambda pricing is based mainly on number of requests and execution duration. Memory size also matters because compute cost is related to configured memory and execution time.

Pricing Factor Meaning
Requests How many times the function is invoked
Duration How long the function runs
Memory How much memory is configured for the function
Architecture x86 or Arm can affect price and performance
Provisioned concurrency Additional cost for keeping environments ready
Total cost depends on:

number of invocations
× execution duration
× configured memory
+ optional features such as provisioned concurrency

Rule of thumb: Lambda is often cost-efficient for irregular, short-running, event-driven workloads. For constantly running high-throughput workloads, containers or servers may sometimes be cheaper.

Advantages and Limitations

Lambda is powerful, but it is not a universal replacement for every backend architecture.

Advantages Limitations
Less infrastructure management Maximum execution time limit
Automatic scaling Cold starts can add latency
Good AWS service integrations Stateless execution model
Useful pay-per-use model Debugging distributed flows can be harder
Good for event-driven systems Service quotas and payload limits matter

Lambda vs Containers

Use Lambda When Use Containers When
You have short event-driven tasks You need long-running services
You want minimal infrastructure management You need more runtime and networking control
Traffic is irregular or spiky Traffic is constant and predictable
Function-level scaling is enough You need full application-level control

AWS Lambda Best Practices

Even beginner Lambda projects should follow a few practical rules. These habits prevent many production problems later.

  • Keep functions small and focused. A function should usually do one clear job.
  • Use environment variables. Do not hardcode table names, bucket names, or endpoints.
  • Write idempotent handlers. The same event may be retried or delivered more than once.
  • Reuse SDK clients. Initialize clients outside the handler when possible.
  • Use structured logging. Logs should help you debug real production issues.
  • Configure timeouts intentionally. Do not leave default values without thinking.
  • Grant least-privilege IAM permissions. Give the function only what it needs.
  • Protect downstream systems. Use queues, throttling, reserved concurrency, or RDS Proxy when needed.

Idempotency Example

Idempotency means the same event can be processed more than once without causing incorrect behavior.

def process_order(order_id):
    if order_already_processed(order_id):
        return "already_processed"

    charge_customer(order_id)
    mark_order_as_processed(order_id)

    return "processed"

Least-Privilege IAM Example

If a function only needs to read files from one S3 bucket, do not give it full AWS admin access.

{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject"
  ],
  "Resource": [
    "arn:aws:s3:::example-bucket/*"
  ]
}

Real-World Examples

REST API Backend

Browser -> API Gateway -> Lambda -> DynamoDB
  • User sends an HTTP request.
  • API Gateway invokes Lambda.
  • Lambda validates input and reads or writes data.
  • Lambda returns an HTTP response.

Image Processing

Upload Image -> S3 -> Lambda -> Resize Image -> Save Thumbnail
  • User uploads an image to S3.
  • S3 sends an event to Lambda.
  • Lambda downloads the file.
  • Lambda creates a thumbnail or extracts metadata.

Queue Worker

Application -> SQS Queue -> Lambda Worker -> Database / External API
  • Application sends a job to SQS.
  • Lambda reads messages from the queue.
  • Lambda processes each message.
  • Failed messages can be retried or moved to a dead-letter queue.

Scheduled Job

EventBridge Schedule -> Lambda -> Cleanup / Report / Sync
  • Run cleanup every night.
  • Generate reports every morning.
  • Synchronize data every hour.
  • Check system health periodically.

Conclusion

AWS Lambda is a practical way to run code without managing servers. It works especially well for APIs, file processing, background jobs, scheduled tasks, queue workers, and event-driven architectures.

The core idea is simple: an event happens, Lambda runs your function, and your function performs a task. Behind that simple model, there are important concepts such as cold starts, warm starts, concurrency, retries, IAM permissions, logging, and downstream limits.

Key takeaway: Lambda is best for short-running, focused, event-driven workloads. It can reduce operational overhead, but it still requires good engineering around reliability, observability, permissions, and system design.

Next Articles to Read

This article covered the fundamentals of AWS Lambda. As you start building production serverless applications, the following topics are worth exploring in more depth:

Comments (1)

The function itself is the easy part—the real pain is around retries, idempotency etc.