A quick quiz.
I write a web app to switch the workplace coffee machine on or off. (I leave to you the writing of the sibling application that fills the machine with coffee and water.) The user interface consists of just one page. It has a big label that says “ON” or “OFF” and a big button that says “Toggle”. When the user presses the button, the coffee machine switches on (or off) and the page is reloaded, with the ON/OFF status updated, of course. I implement the toggle button form like this:
<form method="post" action="toggle"> <button>Toggle</button> </form>
What is wrong with this?
The problem lies with the toggle action. It emerges from a combination of two things:
- The presumed result of the action depends on the state of the user interface.
- The actual result of the action depends on the state of the server.
Unfortunately, with web apps, there is no guarantee that the state of the user interface corresponds to the state of the server. For example, two people might be using the application at the same time. It would go like this:
- Alice surfs to the coffee page. The coffee machine is off.
- Bob surfs to the coffee page. The coffee machine is still off.
- Alice presses the toggle button. The coffee machine switches on.
- Bob’s page still shows that the machine is off. He presses the toggle button, thinking the machine will switch on. Instead, the coffee machine switches off.
- Nobody gets coffee and everybody blames Eve as usual.
Here’s the form fixed:
<form method="post" action="toggle"> <input type="hidden" name="new_state" value="on" /> <button>Switch on</button> </form>
Now there is no ambiguity. “Switch on” means “Switch on”. If the coffee machine already happens to be on, it is just as fine.
Actions of this kind are called idempotent. The outcome of an idempotent action remains the same even when the action is repeated. Because of the disconnect between the UI and the server, action idempotency is very desirable in web applications.
Another teaching we can draw from the example regards session state. Session state consists of things like the location of the user in the user interface, and the paths he can take from there. Resource state, in contrast, is a more permanent variety of state that exists independently of sessions. In the coffee machine example:
- Session state: The fact that the toggle button will turn the coffee machine on.
- Resource state: The fact that the coffee machine is off.
The problem with the coffee app was saving the session state on the server, invisible to the user. The general guideline: Don’t hide session state on the server.