I3.05.3UI debounce is not idempotencydesign

Disabling the button is not idempotency

Aliases: frontend anti-duplicate · client-only dedupe · button disable

What it is

Disabling the button the instant of submit will stop a double tap on that screen. It will not stop: a second entry after timeout, another device, a second webview the OS restored, a scripted replay, a payment channel’s callback retry. UI anti-duplicate is not idempotency. Idempotency is the server returning the first result for the second arrival of the same intent. UI anti-duplicate is fewer arrivals. Very few is still not zero. Zero is a server guarantee.

The split from “disable the submit entry on first click” sits here: that layer stops the second tap in the hand; this layer is the copies that still fly onto the network after the hand was stopped.

Why it happens

UI state lives in one process, one heap, one page lifetime. The uncertainty window outlives the process: a killed app, a reclaim, a notification that opens another instance. The new instance brings a clean tappable button; the anti-duplicate memory is empty. The network path also copies without the client: a load-balancer retry, a gateway timeout resend, a partner webhook delivered at-least-once. Those arrivals never pass through that button.

So the defence is layered. The outer layer reduces hand error (disable, throttle); the middle names the intent (client key); the inner executes under that name as a constraint. Pull the inner layer out and the outer two are filters, and filters leak. The leak on a payment or an order-create is an extra charge. Writing “we disabled the button” into acceptance treats a filter as a sealed pipe — a category error.

Where it stops holding

Side-effect-free requests (refresh, search) may use UI anti-duplicate only, to cut traffic; they do not need a server key. Local-only writes have no server. Deliberate rapid “another one” from several people is new intent: the server should execute, and the UI should not freeze so hard that a burst cannot be sent — here both anti-duplicate and idempotency distinguish by key, rather than killing the button. Internal tools, small amounts, scenes that can be unpicked by hand sometimes run on UI anti-duplicate plus later reconciliation; reconciliation is not idempotency, it is compensation, and compensation has a time window and a labour cost. Public APIs and open-replay channels (payments, SMS, mail) must assume more arrivals than clicks the UI saw.

Applying it

  • List “disable the button” as a way to cut double taps. In acceptance, list separately “the same idempotency key arriving twice, the business happens once”.
  • On money, outbound send, or unique resource creation, a server with no key or no unique constraint must not ship, even if the frontend already disables.
  • Do not implement server logic as “ignore if two clicks were closer than N ms”. A legitimate retry often lands seconds to minutes later.
  • How to check: with the button disabled, proxy the same request (same key) twice; the business count must be 1. Then a client with no frontend (curl, a second device, a payment callback) sends the same key; still 1. Finally twice without a key: should be 2 — proof that without server protection the UI cannot hold the line.

Related

  • Same group: I3.05.1 Network uncertainty makes retry inevitable · I3.05.2 Idempotency keys are minted on the client
  • Nearby: H1.07 Preventing Duplicate Submit · I3.02 Optimistic updates
  • Search terms: idempotency · client-side debounce · server-side dedupe

Cards in the same group

Quick Actions

Share

Share this page

ios_share

https://hci.top/en/handbook/I3.05.3