Ever written a function so clean and predictable you could set your watch to it? It takes an input, does its job, and gives you the exact same output every single time. It’s simple, reliable, and easy to scale. But then, the requirements change. Suddenly, that function needs to remember what happened the last time it ran. It needs a memory. It needs state.
The choice between stateless and stateful processing impacts everything from scalability and complexity to reliability. And with the rise of serverless computing, understanding these concepts is essential for building modern, efficient applications. Let's break down what these terms actually mean for a developer, what happens when they collide, and how you can get the best of both worlds.
Let's start with the simplest option: stateless processing. Think of it like a basic calculator. If you type in 2 + 2, you get 4. It doesn't matter if you did it a second ago or a year ago. The output depends only on the current input.
In development, this means the function has no memory of past events and doesn't rely on any external data to do its job. Examples unclide:
As long as the input is the same, the output will always be the same. Developers call these pure functions.
Why does this matter? Because there's no shared memory or state to manage, you can run a million instances of a stateless function side-by-side without them ever interfering with each other. This makes horizontal scaling a breeze and retrying a failed operation completely safe (a property known as idempotency).
Stateful processing is the opposite. It has a memory. The outcome of an operation depends not just on the input, but also on a pre-existing, external state.
The classic example is an e-commerce shopping cart. When a user adds an item to their cart, the function needs to know what’s already in the cart to calculate the new total. The "cart" is the external state. Counting website clicks or managing a user's session are other common examples.
This introduces a side effect: the function modifies something outside of itself. Adding the same item to the cart twice results in a different state than adding it once.
And that's where the big challenge with stateful processing comes in: consistency. You have to protect that state from being corrupted. What happens if two users try to book the last available seat on a flight at the exact same time? Your application needs to handle that gracefully, which adds complexity.
So where does "serverless" fit into all this?
Despite the name, there are still servers. The difference is that as a developer, you don't have to manage them. Serverless platforms like AWS Lambda, Azure Functions, or frameworks like Spring Cloud Functions let you write and deploy individual functions, and the platform handles the rest. It automatically spins up resources when your function is called and scales them down when it's not.
You get to focus purely on the code. For example, here's a complete Spring Cloud Function application. We only need to provide a simple function, and the framework takes care of running it on demand.
You might notice that this uppercase function is stateless. And that’s no coincidence. Serverless architectures and stateless functions are a perfect match because they scale so effortlessly. Need to handle more traffic? The platform just spins up more independent instances. This automatic scaling is often called elasticity.
Now that we’ve explored the differences between stateless, stateful and serverless processing, let’s look at an interesting use case where they intersect. What happens when your application needs to be both stateful and elastic?
You can absolutely run a stateful application in a serverless model. The common approach is to have each function instance connect to a shared database (the external state), either when the application starts or when the data arrives. Both approaches have their pros and cons, but you will end up with something like this when you spin up multiple instances of your application.
While this works, you quickly run into bottlenecks. As you scale up, your database becomes the limiting factor. You start asking tough questions:
In other words: serverless solves the computational scaling problem, but it doesn't magically solve the state scaling problem.
So, are we stuck choosing between simple scaling and complex state management? Not necessarily. A new wave of "stateful serverless" platforms promises to tackle this exact problem. One interesting example we've worked with is Kalix (formerly Akka Serverless).
The core idea is to bring the state closer to the function. Instead of you having to manage database connections, the platform "injects" the relevant state directly into the function alongside the event that triggers it. Your job is simply to write a function that defines how an event changes the current state to produce a new one.
The Kalix platform handles all the heavy lifting behind the scenes:
This approach hides the state management complexity from the developer, letting you build elastic, stateful applications without getting bogged down in database bottlenecks and concurrency issues.
Obviously, there is more to it than just this small code snippet, but this illustrates what people mean when they talk about stateful serverless applications.
Understanding the trade-offs between stateless and stateful design is crucial for anyone building scalable systems. To recap, here's what each processing option does and when you should use it.
Luckily, emerging platforms like Kalix show a promising future where we don't have to make such a hard choice. By abstracting away state management, they let us build powerful, elastic applications that can handle complex, real-world requirements.
Not sure which processing option would be best for your next project? Need an expert's eye to take a critical look at your architecture? Get in touch to discuss how we can help.