A timeout is not a no.
When a request times out, the caller knows only that it did not receive a response. The remote system may have rejected the work, still be processing it, or have completed it successfully. Treating every timeout as permission to repeat the operation is how duplicate work enters a system.
scroll ↓ uncertainty needs a name
A retry only becomes safe when the operation can be recognised.
Name the operation.
For important actions, give the work a stable identity before sending it. That identity can be used to recognise a repeated request and return the already-created result instead of performing the action again. This is the practical value of idempotency.
Make uncertainty visible.
Some operations cannot be confirmed immediately. Design for that state explicitly: record the request, expose its status, and make the eventual outcome discoverable. A clear “processing” state is often more honest—and more useful—than pretending a timeout means nothing happened.
Retries also need limits, backoff, and good observability. Those details stop a temporary dependency problem from turning into a self-inflicted traffic spike. More importantly, they make the recovery path understandable for both the system and the people operating it.