Skip to content
ms.MUHAMMAD
ABU SAYEED
WorkAboutExperienceWritingPhotographyContact
Menu +
WorkAboutExperienceWritingPhotographyContact
ms.

© 2026 Muhammad Abu Sayeed

GitHub LinkedIn Contact
← All writing
FIELD NOTES / 08Backend Development

Designing for the second click

Timeouts happen. Retries happen. Idempotency is how an API remembers what the user actually intended.

ms.
Muhammad Abu Sayeed10 · ix · 2026 / 3 min read
N° 08
IN THIS NOTE
01Identify the operation, not the attempt02Concurrency is the difficult part03A database transaction cannot cover everything04Test the ambiguous moments
Reading progress 0%

A user submits a request. The server completes it, but the response disappears on the way back. From the browser's perspective, the operation failed. From the database's perspective, it succeeded. The next click is not careless behaviour; it is a reasonable response to uncertainty. An API needs to account for both perspectives.

Identify the operation, not the attempt

Consider an export service. A person asks for one report and retries after a timeout. If every request creates a new export, the service can produce duplicate files, send duplicate emails, and charge twice for expensive processing. An idempotency key lets the client identify the same intended operation across multiple attempts.

The key should not silently turn different requests into the same action. Associate it with the authenticated scope and a fingerprint of the relevant input. If a caller reuses it with incompatible parameters, reject the conflict rather than returning an unrelated result. Stripe documents this parameter comparison in its own API; the exact retention and error semantics remain specific to each implementation.

Concurrency is the difficult part

A process-local dictionary is not a sufficient solution when two application instances receive the same key simultaneously. Both may observe “not found” and start the operation. The claim on a key needs an atomic boundary, often enforced with a database uniqueness constraint and a transaction.

A conceptual record could include the caller, key, request fingerprint, operation status, and result reference. One request wins the right to create the operation. Another can receive the completed result or a clearly documented in-progress response. Avoid promising that every duplicate immediately gets a finished answer when the original work may still be running.

A database transaction cannot cover everything

Suppose the operation calls an external email provider. Recording “sent” before the call risks losing the email if the process crashes. Recording it afterwards risks sending twice if the provider accepts the message and the process crashes before recording success.

This boundary deserves explicit design. Use a provider's idempotency capability where available, or model the work as a durable delivery intent with reconciliation. A local transaction alone does not make a remote side effect exactly once. Be precise about what the service can guarantee and what remains uncertain.

Test the ambiguous moments

Test two concurrent submissions with the same key, a retry after success, a retry with different input, and a failure between each important state change. Also decide how long operation records remain available. After expiry, the same key may no longer protect against duplication; clients need to understand that contract.

The visible result is modest: a button that behaves sensibly when the network does not. The engineering underneath is valuable precisely because the user should not have to think about it.

Further reading: Stripe: Idempotent requests and PostgreSQL: Unique constraints.

ms.END OF NOTE
CONTINUE EXPLORINGAll notes
AI Engineering

Chunking is an information design problem

Backend Development

Background jobs need a memory