Stateless and stateful architecture defines the user experience in specific ways. See why stateless is the choice for cloud architects.
Stateful services keep track of sessions or transactions and react differently to the same inputs based on that history. Stateless services rely on clients to maintain sessions and center around operations that manipulate resources, rather than the state.
Two potential points for confusion in considering stateless and stateful architectures are:
Historically, clients or end-user machines were “thin”--very limited in both hardware and software, while all of the heavy lifting was done by powerful servers. Therefore, it made sense for nearly all complexity and functionality to be handled “server-side”.
With the explosion of the world wide web and the related growth of the HyperText Transfer Protocol (HTTP), web browsers, PCs, and other end-user software and hardware became more powerful, and clients became “thicker”; they handled more and more functionality that was previously handled by servers.
As software and digital systems grew in complexity, so did the requirements for sessions. Because HTTP is a stateless protocol, it’s challenging for software to carry out long-running transactions, where system behavior depends on what happened previously.
For example, when you use online banking, you probably send several instructions to your bank, a simplified version of which, might be similar to:
In order to correctly execute the last two steps, your bank has to remember that step 1 was successful. Because HTTP (the protocol that your web browser uses to talk to your bank) is stateless, this isn’t as simple as it may initially seem.
If you’ve seen the film Finding Nemo, you can think about using HTTP for any multi-step process as similar to trying to have a conversation with Dory who is incessantly forgetful.
There are two primary ways to handle this problem of “forgetfulness”:
There are challenges with each of these approaches.
With the stateful approach, more care has to be taken so that information stored on the server is consistent and so that a specific server is reserved for a client for the entire transaction.
With either approach, it’s necessary to use cryptography to ensure that the client isn’t lying about the previous steps in the transaction (for example, faking a login).
Prior to the modern enhancements of the internet, interactions across the web were typically done using stateful architectures. This was for a number of reasons, including:
As all of these have changed, there has been a move from stateful to stateless architectures.
The paradigm shift from stateful to stateless architectures is closely linked to the popularity of different ways to build application programming interfaces (APIs) and web applications. In the early 2000s, SOAP (Simple Object Access Protocol) was the dominant methodology. While it’s possible to create stateless SOAP-based architectures, the protocol also allowed for creating stateful services, maintaining state server-side. Many applications ultimately did this.
REST (REpresentational State Transfer) is a design pattern rather than a protocol like SOAP, and RESTful services can be built in different ways. Following this pattern, which is always fully stateless, RESTful grew in popularity and overtook SOAP.
GraphQL is the new kid on the block, starting to eat REST’s lunch. Like RESTful services, GraphQL-based services are entirely stateless, and give clients even more control over what data is fetched from servers.
Incorporating a stateless solution to a web service or app has a number of advantages over an equivalent stateful implementation.
For starters, stateful server implementations are complex to configure. They require the ability to reference (potentially multiple) states, which could lead to a number of incomplete transactions and sessions throughout a system. Connection management from the client to the server is also a factor: it requires a “sticky” connection between each client and server pair, which is an inherently a hard-to-scale limitation.
One of the biggest drawbacks of stateful architecture is its inability to scale easily and efficiently. If the state is managed server-side, it means each step in a long-running transaction has to be handled by the same server.
For small-scale services where only a single physical server is required, it’s easier to keep track of state. However, if more servers are required to handle the load from thousands or millions of users, it’s inconvenient to
With a stateless architecture, a user could hit a different server on each step of the transaction because it doesn’t matter if one of the servers is too busy or goes down; the transaction can continue without issue.
The independence or loose coupling between client and server allows for more flexibility when developing a solution that would, otherwise, rely heavily on server-side complexity that is characteristic of stateful systems.
What this means for modern-day technology stacks is that they have become far more modular. Stateless architectures have increasingly advocated for the use of smaller, simpler microservices, which help move us away from the monolithic solutions of the past.
While stateless services don’t necessarily equate to microservices or cloud-based solutions, there is a correlated trend. A business that has already implemented stateless services can more easily break separate functionality into different microservices. And a business that has already implemented microservices can more easily do a staged migration to the cloud, moving one service at a time without impacting the others.
Compared to older patterns of monolithic, stateful services, this makes it easier to keep up with more modern demands of scaling globally and following agile methodologies to release new features far more frequently than was previously expected.
This, in turn, allows for easier horizontal scaling of services. Adding more servers to a fleet is far more straightforward if these servers don’t have to worry about tracking state, and this can result in cost savings for the business as they can quickly provision more servers to deal with demand peaks. Following these patterns, an e-commerce site, for example, can double or triple its capacity when there is demand, without having to pay for additional computing power when demand decreases.
Based on these factors, choosing a stateless architecture for any modern-day, online service is almost always the correct path.
Taking a look at a practical example is a good way to demonstrate the differences discussed.
While this example may be oversimplified, consider the key overarching takeaways of each implementation.
To add more detail to the previously discussed online banking example, let’s take a look at the steps a stateful server would have to carry out for a basic transaction. For each of these steps, the service would have to maintain an explicit state graph, tracking which state the user is currently in and which states they can access next.
Let’s entertain the transaction:
This entire process is confined within a single session. All of the state information is retrieved and tracked in a synchronous way. If there is a connection problem or logical error in any of the steps above, the entire state needs to be recreated in order to pick up where a user left off.
If any changes were to be made to the client-side application, these would need to be taken into consideration, as it is tightly coupled with the server-side functionality.
Taking the same online banking example, let’s entertain it in a stateless model.
In some ways, this appears similar to the first example. However, the subtle differences have a big impact on the overall robustness of the solution.
By utilizing authentication tokens, there is no dependency on a single session in order for the client to interact with the server. The authentication token effectively carries the state, so we do not rely on any individual server. This means that each step can happen in isolation from the other steps, and different infrastructure can be used in each case.
Stateful architecture made sense in a server-focused world where clients were merely thin interfaces to more powerful servers. When services only needed to scale to hundreds or thousands of users, having a strong coupling between users and servers wasn’t an issue.
Now that we have powerful client machines and web services are often required to scale to millions or even billions of users, we’ve needed to evolve not only hardware and software, but also design patterns and concepts.