00:00

Serverless Architecture

Last updated:

Serverless architecture is a way of building and running applications without managing servers yourself. You write small pieces of code, upload them to a cloud provider, and the provider runs them whenever they are needed, adding capacity automatically when traffic rises and removing it when traffic stops. You pay only for the work your code actually does.

This tutorial explains what serverless really means, how it works, its building blocks, cold starts, how it compares with traditional servers and containers, and when it is (and is not) a good choice. It also shows practical, formatted Java examples using AWS Lambda and Spring Cloud Function.

1. What Is Serverless Architecture?

Serverless architecture is a cloud computing model in which applications run in response to events and scale automatically. Instead of running a server all day, you write small, single-purpose pieces of code called functions. A function runs only when something triggers it, such as an HTTP request, a file upload or a new message in a queue, and stops when its work is done.

The cloud provider takes care of everything below your code: the servers, the operating system, the runtime, scaling, patching and availability.

Who manages what: cloud virtual machines versus serverless Two stacks of six layers: physical servers, operating system, runtime, scaling, availability, and your code. With cloud virtual machines, your team manages everything from the operating system up to your code, while the provider runs the physical servers. With serverless, the cloud provider manages every layer except your code. CLOUD VIRTUAL MACHINES SERVERLESS Your code Availability and failover Scaling and capacity Runtime (e.g. Java) Operating system and patches Physical servers Your code Availability and failover Scaling and capacity Runtime (e.g. Java) Operating system and patches Physical servers managed by your team managed by the cloud provider
Figure 1: With virtual machines, your team still manages the operating system, runtime and scaling. With serverless, your team owns only the code and the provider manages everything underneath it.

2. Why Is It Called "Serverless"?

The name is a little misleading: servers still exist. "Serverless" means you do not see or manage them. You never choose a machine size, install an operating system or decide how many servers to run. From a developer's point of view, the servers have disappeared, even though the provider still runs plenty of them behind the scenes.

3. How Serverless Architecture Works

  1. Write a function that does one specific job, such as "resize an image" or "create an order".
  2. Connect it to an event source, such as an HTTP endpoint, a storage bucket, a queue or a schedule.
  3. An event happens, for example a user uploads a photo.
  4. The provider runs the function, starting as many copies as needed to handle all the events at once.
  5. The function finishes and returns a result or writes to another service. When there are no events, nothing runs and you pay nothing for compute.
Event-driven flow in a serverless application Four event sources on the left: an HTTP request through an API gateway, a file upload to storage, a message in a queue and a schedule. Each triggers a function in the middle, which the provider scales from zero to many copies. The functions then use managed services on the right: a database, file storage and a notification service. EVENT SOURCES FUNCTIONS MANAGED SERVICES HTTP requestvia API Gateway File uploadedto cloud storage Message arrivesin a queue Schedulee.g. every night at 2 am Provider scales 0 → N copies createOrder() resizeImage() sendEmail() dailyReport() Database File storage Notifications no events → no running code → no compute cost
Figure 2: Events trigger functions, the provider runs as many copies as needed, and the functions use managed services for storage, data and messaging.

4. Key Components of Serverless Architecture

ComponentWhat it doesExamples
FunctionsSmall, single-purpose pieces of code that run on demandAWS Lambda, Azure Functions
API GatewayTurns HTTP requests into function calls; handles routing, authentication and throttlingAmazon API Gateway, Azure API Management
Event sourcesThings that trigger functionsFile uploads, queues, database changes, schedules
Managed data storesDatabases and storage that scale without serversAmazon DynamoDB, Amazon S3, Azure Cosmos DB
MessagingConnects functions and services asynchronouslyAmazon SQS and SNS, Amazon EventBridge
Identity and permissionsControls what each function is allowed to accessAWS IAM roles, Azure managed identities
MonitoringCollects logs, metrics and tracesAmazon CloudWatch, AWS X-Ray, Azure Monitor

5. FaaS vs BaaS

Serverless covers two related ideas:

  • FaaS (Function as a Service): you write the code, and the platform runs it in response to events. AWS Lambda and Azure Functions are FaaS.
  • BaaS (Backend as a Service): ready-made backend features you use directly, without writing or running server code, such as authentication, databases, file storage and push notifications. Firebase and Amazon Cognito are common examples.

Most serverless applications combine both: functions hold the business logic, and managed services provide the rest.

6. Serverless vs Traditional Servers vs Containers

AspectTraditional servers / VMsContainersServerless functions
You manageOS, runtime, scaling, patching, codeContainer images, orchestration settings, codeCode and configuration only
ScalingManual or auto-scaling groupsOrchestrator scales containersAutomatic per request, including to zero
BillingPer server, running or idlePer node or container, running or idlePer request and execution time
Startup timeMinutesSecondsMilliseconds to a few seconds (cold start)
Long-running workYesYesLimited (for example, 15 minutes per call on AWS Lambda)
ControlFullHighLimited
Best forLegacy apps, special hardwareMicroservices with steady trafficEvent-driven work, spiky or low traffic

7. Function Lifecycle and Cold Starts

When a function is called and no ready copy exists, the provider must first prepare one: start a small environment, load the runtime and your code, and run your initialisation logic. This delay is called a cold start. Later calls reuse the prepared copy and skip these steps; these are warm starts.

Cold start compared with warm start A cold start has four steps before the response: create the environment, start the runtime, run initialisation code, then run the handler. A warm start reuses the existing environment and only runs the handler, so it is much faster. Cold start Create environment Start runtime Run init code Run handler Warm start Run handler reuses the prepared environment request arrives time → The extra steps happen only when a new copy is needed.
Figure 3: A cold start adds environment setup, runtime startup and initialisation before your handler runs. A warm start goes straight to the handler.

Cold starts are often a few hundred milliseconds, and can be longer for large applications or runtimes like the JVM. Ways to reduce them:

  • Keep packages small and avoid heavy work during initialisation.
  • Create clients once, outside the handler method, so warm calls reuse them.
  • Give the function more memory; providers also give it more CPU, so initialisation finishes faster.
  • Use provisioned concurrency to keep a number of copies ready in advance, for latency-sensitive endpoints.
  • For Java on AWS Lambda, enable SnapStart, which starts new copies from a snapshot of an already initialised environment, or compile to a native image with GraalVM.

8. Writing a Serverless Function in Java

On AWS Lambda, a Java function is a class that implements RequestHandler. Add the Lambda libraries to your Maven project (check Maven Central for the latest versions):

pom.xml
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-events</artifactId>
    <version>3.11.4</version>
</dependency>

This function handles an HTTP request coming through API Gateway and returns a JSON greeting:

HelloHandler.java
package com.javabytechie.serverless;

import java.util.Map;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;

public class HelloHandler
        implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    @Override
    public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent request,
                                                      Context context) {
        // Read an optional query parameter: /hello?name=Asha
        Map<String, String> params = request.getQueryStringParameters();
        String name = (params != null && params.get("name") != null) ? params.get("name") : "World";

        context.getLogger().log("Greeting " + name + ", request id " + context.getAwsRequestId());

        return new APIGatewayProxyResponseEvent()
                .withStatusCode(200)
                .withHeaders(Map.of("Content-Type", "application/json"))
                .withBody("{\"message\": \"Hello, " + name + "!\"}");
    }
}

Package the code as a JAR, create a Lambda function with a Java runtime (such as Java 21), set the handler to com.javabytechie.serverless.HelloHandler::handleRequest, and connect it to an API Gateway route. Each HTTP request now runs this method, and AWS adds more copies automatically as traffic grows.

9. Event-Driven Example: Processing Uploaded Files

Functions are not just for HTTP. This one runs automatically whenever a file is uploaded to an Amazon S3 bucket, for example to record new invoices. Notice that the client is created once, outside the handler, so warm calls reuse it:

UploadProcessor.java
package com.javabytechie.serverless;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord;

public class UploadProcessor implements RequestHandler<S3Event, String> {

    // Created once per environment and reused by every warm invocation
    private final InvoiceRepository invoices = new InvoiceRepository();

    @Override
    public String handleRequest(S3Event event, Context context) {
        LambdaLogger logger = context.getLogger();
        int processed = 0;

        for (S3EventNotificationRecord record : event.getRecords()) {
            String bucket = record.getS3().getBucket().getName();
            String key = record.getS3().getObject().getUrlDecodedKey();   // e.g. "invoices/inv-1001.pdf"
            long size = record.getS3().getObject().getSizeAsLong();

            if (!key.endsWith(".pdf")) {
                logger.log("Skipping non-PDF file: " + key);
                continue;
            }

            // Idempotent: saving the same key twice does not create a duplicate
            invoices.saveIfAbsent(bucket, key, size);
            processed++;
        }

        logger.log("Processed " + processed + " file(s)");
        return "OK";
    }
}

Events can be delivered more than once, so the function is idempotent: processing the same file twice has the same effect as processing it once.

10. Serverless with Spring Boot: Spring Cloud Function

If you prefer Spring, Spring Cloud Function lets you write business logic as plain Java functions and run the same code on AWS Lambda, Azure Functions or as a normal web application. Your code does not depend on any one provider's API:

OrderFunctions.java
package com.javabytechie.serverless;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.function.Function;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class OrderFunctions {

    public record OrderRequest(String productId, int quantity, BigDecimal unitPrice) { }

    public record OrderQuote(String productId, int quantity, BigDecimal total) { }

    public static void main(String[] args) {
        SpringApplication.run(OrderFunctions.class, args);
    }

    /** Calculates the total price of an order, including 18% tax. */
    @Bean
    public Function<OrderRequest, OrderQuote> quoteOrder() {
        return request -> {
            if (request.quantity() <= 0) {
                throw new IllegalArgumentException("Quantity must be positive");
            }
            BigDecimal total = request.unitPrice()
                    .multiply(BigDecimal.valueOf(request.quantity()))
                    .multiply(new BigDecimal("1.18"))
                    .setScale(2, RoundingMode.HALF_UP);
            return new OrderQuote(request.productId(), request.quantity(), total);
        };
    }
}

To deploy on AWS Lambda, add the spring-cloud-function-adapter-aws dependency and set the Lambda handler to org.springframework.cloud.function.adapter.aws.FunctionInvoker. Locally, with the web starter, the same function is available as an HTTP endpoint, which makes testing easy.

11. Designing Serverless Applications

  • Keep functions small and focused. One function, one job, such as "resize image" or "create order".
  • Make functions stateless. A copy may be created or destroyed at any time, so never keep important data in memory between calls. Store state in a database, cache or storage service.
  • Make functions idempotent. Events can arrive more than once; use unique IDs so repeats are harmless.
  • Prefer asynchronous flows. Use queues and events between functions instead of long chains of direct calls.
  • Set timeouts and retries thoughtfully, and send messages that keep failing to a dead-letter queue so they can be inspected later.
  • Protect downstream systems. Thousands of parallel copies can overwhelm a database, so limit concurrency or use connection pooling services such as Amazon RDS Proxy.
  • Grant least privilege. Give each function only the permissions it needs.

12. How Serverless Pricing Works

Most FaaS platforms charge for two things:

  • Number of requests: a small charge per million invocations.
  • Compute time: how long your code runs multiplied by the memory you assigned, measured in GB-seconds.
Compute usage example
Requests per month : 3,000,000
Memory per function: 512 MB = 0.5 GB
Average run time   : 200 ms = 0.2 s

Compute usage = 3,000,000 x 0.2 s x 0.5 GB = 300,000 GB-seconds per month
Monthly bill  = (requests x price per request) + (GB-seconds x price per GB-second)

Prices differ by provider and region, and many offer a monthly free allowance, so check the current price list. The key point is that idle time costs nothing. For workloads that run at high, steady load all day, however, always-on servers or containers can become cheaper than paying per request.

13. Advantages of Serverless Architecture

  • No server management: no machines to provision, patch or monitor.
  • Automatic scaling: from zero to thousands of parallel executions, and back down, without configuration.
  • Pay for use: you are charged only when code runs, which suits spiky or low traffic.
  • Faster development: teams focus on business logic and ship small features quickly.
  • Built-in availability: providers run functions across multiple data centres.
  • Natural fit for events: integrations with storage, queues, databases and schedules come ready-made.

14. Disadvantages and Limitations

  • Cold starts: the first request to a new copy can be noticeably slower.
  • Execution time limits: functions must finish within a maximum time, such as 15 minutes per invocation on AWS Lambda.
  • Vendor lock-in: triggers, permissions and managed services are provider-specific, which makes moving harder.
  • Harder debugging and testing: work is spread across many small functions and managed services, so you need good logging and distributed tracing.
  • Limited control: you cannot tune the operating system or keep long-lived connections and background threads.
  • Cost at steady high load: constant heavy traffic can cost more than reserved servers or containers.

15. When to Use Serverless (and When Not To)

Good fitPoor fit
Traffic that is unpredictable, spiky or often lowConstant, very high traffic around the clock
Event-driven tasks: uploads, messages, schedulesLong-running jobs that exceed time limits
New products and prototypes that must ship quicklyApplications needing special hardware or OS control
Glue code connecting cloud servicesWorkloads needing consistently very low latency where cold starts are unacceptable
Small teams without operations staffSystems that must run on several clouds or on-premises without changes

16. Real-World Use Cases

  • REST and GraphQL APIs behind an API gateway for web and mobile apps.
  • File processing: resizing images, creating thumbnails, scanning documents and converting videos when files are uploaded.
  • Scheduled jobs: nightly reports, data clean-up and reminders.
  • Stream and event processing: reacting to orders, payments, sensor readings and log events.
  • Chatbots and webhooks: handling messages from chat platforms and payment providers.
  • Back ends for single-page and mobile apps using managed authentication and databases.

17. Popular Serverless Platforms

PlatformProviderNotes
AWS LambdaAmazon Web ServicesThe most widely used FaaS; deep integration with AWS services; supports Java
Azure FunctionsMicrosoft AzureStrong Java and .NET support; integrates with Azure services
Cloud Run functionsGoogle CloudFormerly called Cloud Functions; event-driven functions on Google Cloud
Cloudflare WorkersCloudflareRuns code at the edge, close to users, with very fast starts
Knative / OpenFaaSOpen sourceServerless-style platforms you can run on your own Kubernetes cluster

18. Serverless in System Design Interviews

Interviewers use serverless to test whether you understand trade-offs, not just definitions. Be ready to:

  • Explain when you would choose functions over containers or servers, using traffic patterns and cost.
  • Describe how you would handle cold starts for a latency-sensitive API.
  • Show how functions keep state in managed databases, caches and storage.
  • Explain idempotency, retries and dead-letter queues for event processing.
  • Discuss how to stop thousands of parallel functions from overloading a database.
  • Mention observability: structured logs, metrics and distributed tracing.

A good example answer: "For image uploads, I would trigger a function from the storage bucket, process the image asynchronously, store the result in object storage and record metadata in a managed database, with failed events going to a dead-letter queue."

19. Best Practices

  • Design for statelessness and idempotency from day one.
  • Initialise expensive objects once, outside the handler, and keep deployment packages small.
  • Choose memory based on measurements; more memory often makes functions faster and sometimes cheaper overall.
  • Set sensible timeouts, retries and dead-letter queues for every asynchronous trigger.
  • Use infrastructure as code, such as AWS SAM, the Serverless Framework, Terraform or AWS CDK, so environments are repeatable.
  • Log in a structured format and enable tracing to follow requests across functions.
  • Keep secrets in a secrets manager, not in code or plain environment variables.
  • Set budget alerts so a bug or traffic surge does not cause an unexpected bill.

20. Common Mistakes to Avoid

MistakeWhat goes wrongBetter approach
Storing state in function memoryData is lost when the copy is recycled, and copies disagreeKeep state in a database, cache or storage
Creating clients inside the handlerEvery call pays the setup cost and runs slowerCreate clients once, outside the handler
Non-idempotent event handlersDuplicate events create duplicate orders or emailsUse unique IDs and "save if absent" logic
One giant function doing everythingSlow cold starts, hard to test and deploySplit into small, focused functions
Long chains of functions calling each other synchronouslyYou pay for waiting time, and failures cascadeUse queues, events or a workflow service
Unlimited concurrency against a small databaseThe database runs out of connectionsLimit concurrency or use a connection proxy

21. Summary

  • Serverless lets you run code without managing servers; the provider handles infrastructure, scaling and availability.
  • Functions run in response to events, scale automatically from zero, and are billed per request and execution time.
  • FaaS runs your code, BaaS provides ready-made backend services, and most applications use both.
  • Cold starts are the main performance trade-off; small packages, reused clients, provisioned concurrency and SnapStart reduce them.
  • In Java, implement RequestHandler for AWS Lambda, or use Spring Cloud Function for portable code.
  • Design functions to be small, stateless and idempotent, and connect them with events and queues.
  • Serverless shines for event-driven, spiky and fast-moving workloads, but is not ideal for long-running jobs or constant heavy load.

Understanding both the strengths and the trade-offs of serverless helps you choose the right tool for each part of a system. Open the FAQ tab for answers to common questions about serverless architecture.