· 15 min read
Moving from serverless to functionless
What if you could build your next serverless application without a single Lambda function? Revamped introduction to the functionless model.

This is a revamped version of my older post from 2023 available here:
From Serverless to Functionless: Super Mario Post
Introduction
We came a very long and windy road from building bare metal servers to invoking serverless functions. Virtuazation for Intel platforms, combined with smart automation, gave us the self-service provisioning. Then someone wrapped this idea in a nice GUI and called it cloud computing.
Largely adopted and popularized for marketing purposes, Cloud was much easier to explain to business executives and consumers than technical terms like grid computing. Besides, giants like Amazon, Google, and Microsoft needed catchy terms to distinguish their new service models.
Function as a Service
AWS has introduced the Lambda service in November 2014. It was huge but tiny: the lightweight environments powered by micro VMs. At that time the term serverless didn’t even exist yet. With Lambda, AWS also introduced new revolutionary pricing model for compute: charged for execution time, counted in miliseconds. They also gave us a generous 1 million executions under Free Tier.
It took more than a year for other cloud providers to catch up. Google followed with alpha version of Google Cloud Functions in February 2026, Microsoft previewed Azure Functions in March 2016, and Cloudflare launched their Workers in September 2017.
What was (and still is) so brilliant about the Function as a Service in general? You can just run your code without provisioning any servers, so the term serverless computing stuck quickly. Trust me, the servers are still there: some real iron blades deep down in the cloud providers’ data centers in secret locations, abstracted as availability zones and regions.
For many years AWS Lambda was an integration service between other AWS managed services. We used functions to connect the dots in many cloud-native solutions, and that was the only way to pass some data between the services. Lambda functions became sort of glue code between different services, just like a boat connecting differen islands on the archipelago. You just needed to build the boat to get from one island to the other.
Serverless Lens
I hope you’re familiar with AWS Well-Architected Framework. If you’ve never heard of it, I would hightly encourage you to explore it. These are the architectural best practices for designing and operating reliable, secure, efficient, cost-effective, and sustainable systems in the cloud.
The Serverless Application Lens focuses on building serverless solutions. Cost-efficiency pillar suggests optimizing your functions over time, preferably by using direct integrations between AWS services, something which was not really possible in the past.
If your Lambda function is not performing custom logic while integrating with other AWS services, chances are that it may be unnecessary.
In other words, you should use serverless functions to transform data, not to transport data between services. Read it again, then let that sink in…
It sparked some curiosity in my mind when I first read it… What if I could build my next serverless application without a single Lambda function? Is it finally feasible to achieve on AWS Cloud? There is only one way to find out: try to build it, or fail and learn in the process.
Functionless
Let me introduce you to so called functionless architecture. I’m sure you’ve probably heard this term before, even as a nerdy joke. Hopefully it will stick and stay with us, just like it happened to the serverless term.
So what is the functionless architecture anyway? It’s a type of serverless architecture emphasizing event-driven, service-based design without the need to write, package, deploy, and manage countless individual serverless functions or containers.
Some of the key characteristic this model:
- Event-driven everything: build around events, which are the triggers that initiate actions, and these actions can be handled by a variety of services, eliminating the need for individual functions
- Managed services: AWS provides a suite of managed services that play central roles in functionless architecture, and these services abstract away much of the operational complexity
- Streamlined workflows: workflows are streamlined through managed services, simplifying complex business processes, so codebase becomes simpler and more focused on business logic
Core Services
If not AWS Lambda then what? Many AWS services can now integrate between each other. The following AWS managed services can directly integrate with numerous of other services and provide you more value and less operational overhead.
- Amazon EventBridge: serverless events router, seamlessly connects and routes events between AWS services, a central nervous system for any event-driven application
- AWS Step Functions: serverless workflow orchestration, simplifies workflows management, allows to build visual workflows to handle complex business processes
- AWS AppSync: serverless GraphQL orchestration and Pub/Sub APIs, simplifies building data-driven applications, connects to multiple data sources (APIs, databases, etc)
- Amazon API Gateway: serverless RESTful and WebSocket APIs that enable real-time two-way communication applications
(Dis)advantages of Lambda
We all love the Lamba service (FaaS), there is no question about it. But it does come with some pros & cons, as many other cloud services. Let’s dive deeper into them.
Key advantages:
- Scalability: AWS Lambda automatically scale functions with the number of requests, ensuring consistent performance
- Cost efficiency: Lambda functions are billed based on the actual amount of resources consumed by the code execution, measured in increments of 100 milliseconds
- Quick deployments: AWS Lambda allows for quick iteration and deployment of code; functions can be updated at a rapid pace, which is beneficial for agile development practices
- Reduced operations: AWS takes care of infrastructure management tasks, which allows developers to focus more on innovation and implementation of business logic
Key disadvantages:
- Cold start latency: Lambda functions may experience cold start latency, which is the time it takes to initialize a function when it hasn’t been used in the for a while or at all (new functions)
- Execution time limit: there is a hard limit of execution time of 15 minutes; however Lambda functions are designed to be short-lived computing resources
- Limited debugging: troubleshooting and debugging Lambda functions, especially in systems distributed across regions or accounts, can be become complicated, coordinating logs and tracing across multiple functions can be time-consuming
- Extra libraries: default runtimes for languages may not include all your favourite libraries and dependencies, so you have to manage deployments of these by yourself
We learnt how to live with these disadvantages over time and design our solutions to take them into consideration. Some of us complained a lot to AWS, with their customer obsession they’ve listened and came back with some mitigation options for each of these disadvantages:
- Cold start latency: you can simply use provisioned concurrency to keep your Lambda functions warm at night; of course coming with extra cost, but you can use it if you need to
- Execution time limit: if your functions fail to execute within 15 minutes limit, there is probably something wrong with your design; you can partition your input data and run multiple functions in parallel (aka parallel processing)
- Limited debugging: good observability starts with good logging, so you can use AWS Powertools for Lambda to get nice structured logs for all your functions, then build logs aggregation, for example with cross-region & cross-account dashboards on AWS CloudWatch
- Extra libraries: deployment of external libraries and their dependencies can be a nightmare, especially in complex ecosystems like NPM; some IaC frameworks like AWS Serverless Application Model (SAM) and AWS Cloud Development Kit (CDK) handle these tasks pretty well
Modernizing serverless solutions
We all have these monolithic Lambda functions doing everything. You start with a small code, then add extra conditions, extra integrations with DynamoDB tables, some S3 buckets, and other services. Before you realize, your Lambda cat is way too fat. I’m sure you can find such functions running right now in your production environments. I can actually hear you nodding right now. And don’t get me wrong, they run some heavy worloads right now, and they run them pretty well, right? However, they don’t follow the modern best practices of AWS Well-Architected framework.
Here are some steps you can consider for migration of your serverless applications:
- Monolithic function: identify your single functions (fat / phat Lambda) containing the application logic and integrated with all necessary downstream resources
- Microservice-like serverless application: decompose your monolithic function into smaller individual functions, each being responsible for and performing specific tasks
- State machine: orchestrate your functions with Step Functions workflows, move some of the obvious logic outside of your function code, and use Amazon State Language to build visual state machines
- Functionless: once you start eliminating unnecessary Lambda functions, you may actually find your Step Functions workflow doing all the computation and data manipulation, without a single function, completely and blissfully functionless!
Alice & Bob Telegrams
To explain the concept of the functionless model, I’ve built a small application. Well, just a backend of the real application, but I promise to build some frontend for this as well.
The application is based on the Alice and Bob model from 1978. Alice and Bob are fictional characters commonly used as placeholder in discussions about cryptographic systems and protocols. The model describes Alice and Bob sending messages between eachother, and there is usually also Eve, who evedrops the conversation, as well as other characters. You may heard about this model in recent articles about concept of quantum communication. But worry not, I will not mention anything quantum again in this article :)
Here I use this concept to build a sort of telegraph simulator. In short, Bob sends telegram messages (events) to Alice. They both work for the same Company (AWS account), but in different offices located in London and Paris (actual AWS regions). The telegraph consists of two telegraph stations (EventBridge buses) located in each region.

The core logic of our application is based on two state machines, the AWS Step Functions standard workflows, which are responsible for the telegram transmissions. Then we have DynamoDB global tables, which act as a Telegram Archive. Why global table? The table data is replicated between two regions, so my state machines can interact with same DynamoDB items from both state machines. Additionally there are some SSM Parameters and SM Secrets, just to showcase ASL integration with multiple services.
This is how it looks like if we wrap it in a nice architectural diagram:

Bob’s Transmission
The first state machine (AWS Step Functions workflow) performs the following actions:
- Validates telegram fields and sets some variables based on the state machine input (JSON)
- Converts the message into telegram format: all uppercase, sentences split with STOP word
- Sets pricing for the telegraph services based on the priority (normal vs urgent)
- Searches for sender & recipient addresses stored in the Parameter Store
- Uses Translate service to translate the message to a chosen language (French)
- Formats the telegram message with additional fields, generate telegram ID
- Saves telegram document as a new DynamoDB item
- Emits telegram document to local event bus, and forwards it to remote bus
Here is the workflow visualization:
As you can see, the state machine interacts with few AWS services completely without invoking any Lambda functions. Pretty cool, huh? Let’s see what happens next. Carefully crafted AWS EventBridge event rule catches all events emitted by the workflow, and sends them across to the English Channel, I mean to the other region.
Alice’s Transmission
Welcome to Paris, where another event rule triggers the second state machine. The workflow performs followin actions:
- Checks for fields in state machine input, like telegram’s category (private vs business).
- Alice is only interested in private messages, preferably from Bob himself. Any other business lands in the Postpone Tray: a SQS queue
- with nothing connected to process the messages
- If it’s a private message, then Alice reads it: sets it’s status as delivered and adds a reaction emoji
- Updates existing item in the DynamoDB table, based on the telegram ID passed in the incoming event
- Updates content of Secret Manager secret, her own Personal Diary
- Emits an extra event to local bus for logging
Here is the workflow visualization:
The last action of the workflow emits events to local bus purely for logging purposes. On both regions there are event rules catching all events flowing through custom event buses, to stream the events to AWS CloudWatch Log Groups. A central dashboard in CloudWatch uses Log Insights to visualize the events from both regions, and it looks like this:

Gotchas
The functionless model works fine in this case, but I do admit that this is based on relatively simple workflows. This architecture surely has some limitations, and sooner or later you’ll hit some glass ceiling. So does this model make any sense at all?
- Limitations of ASL: Amazon States Language is fine for simple logic, but building workflows representing logic of complex business processes in your organization might be an overkill. Your workflow will become super unreadable, and you might end up with a monolythic state machine (trust me, it’s possible). Of course you can split the logic into smaller workflows, and maybe orchestrate them from a… master workflow? You get the idea. Sooner or later you’ll be back at calling Lambda functions inside your state machine, as it will be easier to cover the custom logic and data manipulation in one of the Lambda’s supported languages. However, to call AWS services in another region from your workflow is simply not possible, no cross-region API calls, but you can do that from the Lambda function.
- External libraries: Some services like DynamoDB require transforming JSON data to format consumable by the service. This process is called data marshalling or unmarshalling, and may require a Lambda runtime. In this example I’m relying on relatively flat data structure for my DynamoDB items, and they’re also predictable, so I’m covering the marshalling inside the ASL. Also you may require any other libraries (like Prisma ORM) to talk fluently to your RDS databases. Again, bundled in Lambda functions/layers.
- Pricing model: Standard workflows are charged based on number of state transitions between states of the workflow. That means the more complex the workflow, the more expensice each execution will be. That can add up to your monthly cost, depending on nr of executions. It may be way cheaper to move back some of the logic into your code. There are also Express workflows, which are way cheaper, and designed mostly for logs ingestion, etc. They do have some limitations, and you cannot visualize the executions like with Standard workflows, which is the true power of Step Functions in my opinion.
Key takeaways
Pure functionless model is definitely feasible, if you just need relatively simple workflows to orchestrate few AWS services. This is due to current limitations of the Amazon State Language. However, the ASL is evolving and it’s capabilities will definitely expand over time, making very powerful alternative to scripting languages. Recently added support for JSONata expressions just proves it. AWS Step Functions is definitely one of my favourite services on AWS Cloud.
What I would really like is to encourage you to reimagine your serverless applications, especially the ones you’re about to build. Minimize use of custom Lambda functions if you can, and emphasize integrations of native AWS services.
Some of the core design principles you should follow in development of your serverless solutions:
- Single purpose functions: Functions should be compact, short, single-purpose. Split your monolith function’s code into smaller functions, each taking care of specific task only. For example one Lambda interacting with data in S3 buckets, another one with data in DynamoDB tables, each following least-privilege model by giving corresponding IAM roles access to selected services and resources.
- Orchestration: Orchestrate your application with state machines, not functions. Use AWS SDK integrations to call any AWS service’s API actions directly from Amazon State Language powering your Step Functions workflows. That’s more than 200 services and their APIs at your disposal.
- Efficient processing: Use events to trigger transactions, enabling asynchronous, just-in-time processing. Make sure you design your event-driven application for event delivery failures and event duplicates by utilizing EventBridge Archive and Replays.
Demo time!
If you’d like to play around with this application and see it in action, I’ve got this fancy repo for you!
https://github.com/ServerlessNinja/aws-functionless-alicebob
The repo contains AWS CDK app in TypeScript, which deploys few stacks in 2 separate regions defined in cdk.context.json file (here London and Paris):
{
"regions": {
"bob": "eu-west-2",
"alice": "eu-west-3"
},
"locations": { ... }
}Some useful commands of AWS CDK Toolkit for deploying and trashing this CDK app and its stacks:
# Install all required NPM packages
npm install
# Build the TypeScript project
npm run build
# Synthesize all CDK stacks into CloudFormation templates
cdk synth --all
# Deploy the CDK stacks to your AWS account
cdk deploy --all
# Destroy the CDK stacks from your AWS accounts
cdk destroy --allTo start the execution of the Telegraph, you have to manually emit an event to the custom Event Bridge in Bob’s region (here London). On AWS Management Console go to Amazon EventBridge -> Event buses -> Send events. Select TelegraphStationLondon for event bus, provide Telegraph as source, ComposeTelegram as detail type, and an following JSON document as detail:
{
"from": "Bob",
"to": "Alice",
"message": "Hello darling. Coming to Paris next week. I cannot wait for our dinner date!",
"category": "PERSONAL",
"priority": "URGENT"
}You can follow the TransmissionBob state machine execution and DynamoDB items in Bob’s region, then switch to Alice’s region to follow execution of TransmissionAlice state machine and updated items in DynamoDB tables. In few minutes you should also see the events on the CloudWatch dashboard called Transmissions. What if it didn’t start any executions? You probably emitted the event in the wrong region or on the wrong bus (it happens to me quite often, especially during live demos!).
Resources
Some useful resources for this functionless application and links to AWS documentation:
- Alice & Bob Telegrams (presentation slides) https://speakerdeck.com/jakubgaj/moving-from-serverless-to-functionless
- Alice & Bob Telegrams (AWS CDK App) https://github.com/ServerlessNinja/aws-functionless-alicebob
- Learn how to get started with Step Functions https://docs.aws.amazon.com/step-functions/latest/dg/getting-started.html
- Getting started with the AWS CDK https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html
Until our paths cross again in the cloud shadows…
Serverless Ninja © 2025



