This is a super short piece about what I believe to be the most common source of bugs in all web applications. I am referring to a danger that impinges upon all front-end web developers—a source of error that has persisted since the dawn of the internet. Namely: What happens if a user clicks the button twice?
You can think of any user interaction with a web application as either modifying something behind the scenes (e.g. updating the database) or not modifying anything. Browsing a product’s listing page should be non-modifying, whereas adding that product to the cart should be modifying.
Standard web application design practice wraps up non-modifying actions in link-triggered HTTP GET requests, whereas modifying actions are usually contained within button-triggered POST, PUT, or DELETE requests. It is with this latter group of requests that we need to ask ourselves what effect an accidental double- or triple-click would have.
Let’s take a common example from the ecommerce world: the “Buy” button. When pressed, this initiates a credit card payment process. Behind the scenes, this process involves an external payment provider that, in turn, will run a battery of anti-fraud checks. These take a while to complete, and as a result, the payment request can be sluggish from the customer’s perspective—sometimes brutally so. Consequently it’s possible—probable even—that a customer who is still starting at the same loading cursor fifteen seconds later will be tempted to press the “Buy” button again, perhaps under the impression they didn’t press it correctly the first time. If they do this, you most definitely don’t want to charge the customer a second time. That is absolutely not what your customer expects. As such, both your backend and frontend logic ought to prevent this eventuality from ever happening—the backend by locking that particular order record in the database, and the frontend by temporarily disabling the HTML button so that a second click will not trigger any further requests to your server.
The aforementioned risks are further complicated by the existence of the browser back button. Your users expect this button to return them to where they came from—both visually and conceptually. But for this to work, there is an implicit assumption that every web journey has a return ticket. Unfortunately this assumption simply does not hold. The web is full of one-way flows, such as the payment process we just looked at. If the user presses the back button, it’s possible the browser will resubmit their POST (etc.) requests and data. This has messy consequences, such as:
A user who has already requested that your mail-merge software send an email out to their subscribers would inadvertently queue up a repeat of the same campaign, causing their subscribers to feel harassed.
A customer who has just finished making a purchase but then remembers they entered their address wrong might go back to edit the shipping page, only to inadvertently place a second order and buy their whole basket again.
Although the browser typically warns users whenever their pressing of the back button would resubmit data, I’d argue it’s not enough to rely on this mechanism. After all, not every web user understands or cares. That means you’re better off designing your software so that these incidents cannot possibly hurt you.