I used to work at a startup building procurement software, and one of our constant challenges was the lack of sophistication of our customers. One of my favorite bugs we came across was causing the creation of duplicate widgets. A few customers got duplicate widgets ever time, others did sometimes, others never did. We were not able to reproduce it on our test environments.
Eventually we traced through our server logs and found that these customers were simply submitting 2 requests. More investigation determined that, for this customer, every request was being submitted twice, because this user double-clicked everything. Double submission of an action is especially common for sites that can take a while to respond, but for some users double clicking is simply the default. Think of your application and what might go badly if you clicked every button twice, sending near simultaneous duplicate requests to your server. Chances are it wouldn’t be very good.
There is a class of HTTP operations that can be submitted multiple times without changing the result of the initial call (often referred to in CS as idempotent). In HTTP land, these are* your GETs, PUTs and PATCHes. POST’s and DELETE’s, on the other hand, do not have the same effect when submitted multiple times, likely causing data duplication or errors.
Not only can data become corrupted or errors created, the typical user experience you get when double-submitting every action is often rather bad, even when it’s safe to do so. Think unnecessary page reloads, duplicate “success” messages, etc.
For matters of data integrity, you should implement a solution where a unique key is generated on each page or form and submitted along with any non idempotent requests so that the server can detect duplicates and discard them.
In practice this can become tricky when you’re running multiple servers with separate database connections, and you’ll need to concern yourself with database-enforced unique indexes and recovering from aborted transactions.
When your concern is more about the user experience, a simpler approach can be used of just disabling the button when clicked. This prevents the browser from submitting additional requests and informs the user that their request was sent and they do not need to continue clicking.
To address this problem for Hint, we created an angular directive to block double-clicks on any element in our application. This lets us easily prevent duplicate submission of data throughout our app, and lets me rest a bit easier at night. Read more about our implementation here.
NOTE: By “these are” I really mean “these should be”. While many services, especially newer ones, follow HTTP conventions, many do not. Be careful making any assumptions about an API based on the HTTP Verb it uses.