๐๐ป๐๐ฟ๐ผ๐ฑ๐๐ฐ๐๐ถ๐ผ๐ป
Modern software systems expect failures to happen. Networks can timeout, servers can restart, and requests can fail halfway through execution.
To recover from these failures, systems commonly use retries.
But retries introduce another problem: the same operation may execute more than once.
So the important question becomes:
If an operation runs twice, can our system safely handle it?
This is where idempotency becomes an important software-engineering concept.
๐ง๐ต๐ฒ ๐ฅ๐ฒ๐ฎ๐น ๐ฃ๐ฟ๐ผ๐ฏ๐น๐ฒ๐บ: ๐ ๐ง๐ถ๐บ๐ฒ๐ผ๐๐ ๐๐ผ๐ฒ๐๐ป'๐ ๐ ๐ฒ๐ฎ๐ป ๐๐ฎ๐ถ๐น๐๐ฟ๐ฒ
Imagine a payment request reaches the server and the payment succeedsโbut the response is lost because of a network problem.
Client
โ
Request
โ
Server
โ
Payment succeeds
โ
Response lost
โ
Client timeout
The client doesn't know whether the operation failed or succeeded.
So it retriesโand the same operation may happen again.
The real challenge isn't retrying. It's making retries safe.
๐ช๐ต๐ฎ๐ ๐๐ ๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐?
Idempotency means that repeating the same operation produces the same intended final state as performing it once.
For example:
Set account status = ACTIVE
Whether we run it once or five times, the final state is still:
ACTIVE
But consider:
Add โน100 to account
Running it five times adds โน500.
So the important idea is:
If a request is retried, the system should not accidentally repeat the business effect.
This is why idempotency is important when designing reliable software.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ ๐๐ ๐ก๐ผ๐ป-๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ ๐ข๐ฝ๐ฒ๐ฟ๐ฎ๐๐ถ๐ผ๐ป๐
Not every operation is automatically idempotent.
A simple example of an idempotent operation is:
Set user status = ACTIVE
Running it multiple times still produces:
ACTIVE
A non-idempotent operation changes the result every time it runs:
Add โน100 to balance
Run once โ โน100 added
Run twice โ โน200 added
Run three times โ โน300 added
So when designing a system, we need to ask:
"What happens if this operation runs again?"
๐ฆ๐ถ๐บ๐ฝ๐น๐ฒ ๐ฑ๐ถ๐ฎ๐ด๐ฟ๐ฎ๐บ:
Operation
โ
โโโโโโโโดโโโโโโโ
โ โ
Idempotent Non-idempotent
โ โ
Same result Result changes
when repeated when repeated
๐ช๐ต๐ ๐ฅ๐ฒ๐๐ฟ๐ถ๐ฒ๐ ๐๐ฎ๐ป ๐๐ฟ๐ฒ๐ฎ๐๐ฒ ๐๐๐ฝ๐น๐ถ๐ฐ๐ฎ๐๐ฒ ๐ข๐ฝ๐ฒ๐ฟ๐ฎ๐๐ถ๐ผ๐ป๐
Retries are useful because temporary failures are common in real systems.
Request
โ
Temporary failure
โ
Retry
โ
Success
The problem starts when the first request actually succeeded, but the client never received the response.
First request โ Operation succeeds โ Response lost
โ
Client retries
โ
Operation runs again
The same problem can happen at different layers: clients, APIs, load balancers, message queues, and background workers can all retry work.
Retries ๐ถ๐บ๐ฝ๐ฟ๐ผ๐๐ฒ ๐ฟ๐ฒ๐น๐ถ๐ฎ๐ฏ๐ถ๐น๐ถ๐๐, but without idempotency, they can also create duplicate work.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐๐ฒ๐๐: ๐๐ถ๐๐ถ๐ป๐ด ๐ฎ ๐ฅ๐ฒ๐พ๐๐ฒ๐๐ ๐ฎ๐ป ๐๐ฑ๐ฒ๐ป๐๐ถ๐๐
One common way to make non-idempotent operations safer is to give each request a unique idempotency key.
For example:
Idempotency-Key: payment-abc123
The server stores this key when it successfully processes the request.
If the same request arrives again:
Same key
โ
Already processed?
โ
YES โ Return the previous result
Instead of creating another payment or order, the system recognizes that it has already handled that request.
The key idea is simple:
The request can be repeated, but the business operation should happen only once.
๐๐ผ๐ ๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐๐ฒ๐๐ ๐๐ฐ๐๐๐ฎ๐น๐น๐ ๐ช๐ผ๐ฟ๐ธ
The basic flow is:
Client
โ
Request + Idempotency Key
โ
Server
โ
Check stored key
If the key doesn't exist, the server processes the operation and stores the result.
If the same key appears again for the same operation, the server can recognize it as a repeated request and return the stored result.
New key โ Process โ Store result
Same key โ Find result โ Return result
This approach is useful for operations such as payments, order creation, job submission, and other requests where duplicate execution could cause a problem.
Idempotency keys turn an ๐ผ๐๐ต๐ฒ๐ฟ๐๐ถ๐๐ฒ ๐ฎ๐บ๐ฏ๐ถ๐ด๐๐ผ๐๐ ๐ฟ๐ฒ๐๐ฟ๐ ๐ถ๐ป๐๐ผ ๐ฎ ๐ฟ๐ฒ๐พ๐๐ฒ๐๐ ๐๐ต๐ฒ ๐๐๐๐๐ฒ๐บ ๐ฐ๐ฎ๐ป ๐ฟ๐ฒ๐ฐ๐ผ๐ด๐ป๐ถ๐๐ฒ.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐ฎ๐ป๐ฑ ๐๐ฎ๐๐ฎ๐ฏ๐ฎ๐๐ฒ ๐๐ฒ๐๐ถ๐ด๐ป
An idempotency key is useful only if the system can reliably remember it.
A simple approach is to store the key in the database:
idempotency_key
payment-abc123
When another request arrives with the same key, the system can detect that the operation was already processed.
But checking the key in application code alone is not enough. Two identical requests can arrive at almost the same time.
That's why the database should also enforce uniqueness, for example with a ๐๐ป๐ถ๐พ๐๐ฒ ๐ฐ๐ผ๐ป๐๐๐ฟ๐ฎ๐ถ๐ป๐.
Application logic can detect duplicates, but the database should provide the final protection against duplicate records.
๐ฅ๐ฎ๐ฐ๐ฒ ๐๐ผ๐ป๐ฑ๐ถ๐๐ถ๐ผ๐ป๐: ๐ช๐ต๐ฒ๐ป ๐ง๐๐ผ ๐ฅ๐ฒ๐พ๐๐ฒ๐๐๐ ๐๐ฟ๐ฟ๐ถ๐๐ฒ ๐ง๐ผ๐ด๐ฒ๐๐ต๐ฒ๐ฟ
Now consider two requests using the same idempotency key at almost exactly the same time.
Request A โโโ Check key โ Not found
Request B โโโ Check key โ Not found
Both requests may think they are allowed to continue.
This is called a race condition: the result depends on the timing of concurrent operations.
A unique database constraint can prevent both requests from creating the same record.
Request A โโโ Database โโโ INSERT โ
Request B โโโ Database โโโ DUPLICATE โ
The important lesson is:
๐๐ต๐ฒ๐ฐ๐ธ๐ถ๐ป๐ด ๐ณ๐ถ๐ฟ๐๐ ๐ฎ๐ป๐ฑ ๐ถ๐ป๐๐ฒ๐ฟ๐๐ถ๐ป๐ด ๐น๐ฎ๐๐ฒ๐ฟ ๐ถ๐ ๐ป๐ผ๐ ๐ฎ๐น๐๐ฎ๐๐ ๐๐ฎ๐ณ๐ฒ ๐๐ต๐ฒ๐ป ๐บ๐๐น๐๐ถ๐ฝ๐น๐ฒ ๐ฟ๐ฒ๐พ๐๐ฒ๐๐๐ ๐ฐ๐ฎ๐ป ๐ฟ๐๐ป ๐ฎ๐ ๐๐ต๐ฒ ๐๐ฎ๐บ๐ฒ ๐๐ถ๐บ๐ฒ.
Concurrency needs to be handled deliberately.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐ถ๐ป ๐ค๐๐ฒ๐๐ฒ-๐๐ฎ๐๐ฒ๐ฑ ๐ฆ๐๐๐๐ฒ๐บ๐
Idempotency is not limited to HTTP APIs. It is equally important in background job processing.
Consider a worker processing a job:
Queue
โ
Worker
โ
Process job
โ
Worker fails
โ
Job is retried
The retry may cause the worker to process the same job again.
For example, a worker might successfully update the database and then crash before confirming that the job finished.
The queue may then deliver the job again.
This means a reliable worker should be designed so that ๐ฝ๐ฟ๐ผ๐ฐ๐ฒ๐๐๐ถ๐ป๐ด ๐๐ต๐ฒ ๐๐ฎ๐บ๐ฒ ๐ท๐ผ๐ฏ ๐บ๐ผ๐ฟ๐ฒ ๐๐ต๐ฎ๐ป ๐ผ๐ป๐ฐ๐ฒ ๐ฑ๐ผ๐ฒ๐ ๐ป๐ผ๐ ๐ฐ๐ฟ๐ฒ๐ฎ๐๐ฒ ๐ฎ๐ป ๐ถ๐ป๐ฐ๐ผ๐ฟ๐ฟ๐ฒ๐ฐ๐ ๐ณ๐ถ๐ป๐ฎ๐น ๐๐๐ฎ๐๐ฒ.
This is one reason idempotency is important in systems built with ๐พ๐๐ฒ๐๐ฒ๐, ๐๐ผ๐ฟ๐ธ๐ฒ๐ฟ๐, ๐ฟ๐ฒ๐๐ฟ๐ถ๐ฒ๐, ๐ฎ๐ป๐ฑ ๐ฎ๐๐๐ป๐ฐ๐ต๐ฟ๐ผ๐ป๐ผ๐๐ ๐ฝ๐ฟ๐ผ๐ฐ๐ฒ๐๐๐ถ๐ป๐ด.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐๐ ๐ก๐ผ๐ ๐๐ต๐ฒ ๐ฆ๐ฎ๐บ๐ฒ ๐ฎ๐ ๐๐ ๐ฎ๐ฐ๐๐น๐-๐ข๐ป๐ฐ๐ฒ ๐๐ ๐ฒ๐ฐ๐๐๐ถ๐ผ๐ป
Idempotency does not mean the system physically executes an operation only once.
A request or job may actually be processed more than once.
The goal is different:
๐ ๐๐น๐๐ถ๐ฝ๐น๐ฒ ๐ฒ๐ ๐ฒ๐ฐ๐๐๐ถ๐ผ๐ป๐ ๐๐ต๐ผ๐๐น๐ฑ ๐ป๐ผ๐ ๐ฐ๐ฟ๐ฒ๐ฎ๐๐ฒ ๐บ๐๐น๐๐ถ๐ฝ๐น๐ฒ ๐ฏ๐๐๐ถ๐ป๐ฒ๐๐ ๐ฒ๐ณ๐ณ๐ฒ๐ฐ๐๐.
Execute โ Execute โ Execute
โ
Same intended final state
This distinction is important in distributed systems because guaranteeing that something happens exactly once across multiple systems can be difficult.
Instead, engineers often design operations to be ๐๐ฎ๐ณ๐ฒ ๐๐ต๐ฒ๐ป ๐ฟ๐ฒ๐ฝ๐ฒ๐ฎ๐๐ฒ๐ฑ.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐ถ๐ป ๐ฅ๐ฒ๐ฎ๐น ๐ฆ๐ผ๐ณ๐๐๐ฎ๐ฟ๐ฒ ๐ฆ๐๐๐๐ฒ๐บ๐
Idempotency is useful anywhere a repeated request could create an unwanted result.
Common examples include:
Creating an order
Processing a payment
Submitting a background job
Sending notifications
Updating user information
Handling webhook events
The implementation can differ between systems, but the principle remains the same:
Same operation
โ
Repeated request
โ
Safe final result
This makes the system more reliable when requests are retried or messages are delivered more than once.
๐ง๐ต๐ฒ ๐๐ป๐ด๐ถ๐ป๐ฒ๐ฒ๐ฟ๐ถ๐ป๐ด ๐ ๐ถ๐ป๐ฑ๐๐ฒ๐: ๐๐ฒ๐๐ถ๐ด๐ป ๐ณ๐ผ๐ฟ ๐ฅ๐ฒ๐ฝ๐ฒ๐๐ถ๐๐ถ๐ผ๐ป
The biggest lesson is not simply "use an idempotency key."
The deeper lesson is to design software with the assumption that requests may be repeated.
Before implementing an operation, ask:
What if this request runs twice?
What if it runs three times?
What if the response is lost?
What if two requests arrive together?
These questions help us discover duplicate operations, race conditions, and data-consistency problems before they become production issues.
๐ฅ๐ฒ๐น๐ถ๐ฎ๐ฏ๐น๐ฒ ๐๐ผ๐ณ๐๐๐ฎ๐ฟ๐ฒ ๐ถ๐ ๐ป๐ผ๐ ๐๐ผ๐ณ๐๐๐ฎ๐ฟ๐ฒ ๐๐ต๐ฎ๐ ๐ป๐ฒ๐๐ฒ๐ฟ ๐ณ๐ฎ๐ถ๐น๐. ๐๐ ๐ถ๐ ๐๐ผ๐ณ๐๐๐ฎ๐ฟ๐ฒ ๐๐ต๐ฎ๐ ๐ฏ๐ฒ๐ต๐ฎ๐๐ฒ๐ ๐๐ฎ๐ณ๐ฒ๐น๐ ๐๐ต๐ฒ๐ป ๐ณ๐ฎ๐ถ๐น๐๐ฟ๐ฒ๐ ๐ฎ๐ป๐ฑ ๐ฟ๐ฒ๐ฝ๐ฒ๐๐ถ๐๐ถ๐ผ๐ป๐ ๐ต๐ฎ๐ฝ๐ฝ๐ฒ๐ป.
๐ช๐ต๐ฎ๐ ๐ฆ๐ต๐ผ๐๐น๐ฑ ๐ฎ๐ป ๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐ฅ๐ฒ๐ฐ๐ผ๐ฟ๐ฑ ๐ฆ๐๐ผ๐ฟ๐ฒ?
When a system receives an idempotent request, it needs to remember what happened.
Depending on the system, an idempotency record may contain information such as:
Idempotency Key
User ID
Request Status
Response
Created At
For example:
payment-abc123
โ
COMPLETED
โ
Previous response
When the same key arrives again, the system can return the stored result instead of processing the operation again.
๐ง๐ต๐ฒ ๐ถ๐บ๐ฝ๐ผ๐ฟ๐๐ฎ๐ป๐ ๐ฝ๐ผ๐ถ๐ป๐ ๐ถ๐ ๐๐ต๐ฎ๐ ๐ถ๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐ถ๐ ๐ป๐ผ๐ ๐ท๐๐๐ ๐ฎ๐ฏ๐ผ๐๐ ๐๐๐ผ๐ฟ๐ถ๐ป๐ด ๐ฎ ๐ธ๐ฒ๐โ๐ถ๐ ๐ถ๐ ๐ฎ๐ฏ๐ผ๐๐ ๐ฟ๐ฒ๐บ๐ฒ๐บ๐ฏ๐ฒ๐ฟ๐ถ๐ป๐ด ๐๐ต๐ฒ ๐ผ๐๐๐ฐ๐ผ๐บ๐ฒ ๐ผ๐ณ ๐๐ต๐ฒ ๐ผ๐ฝ๐ฒ๐ฟ๐ฎ๐๐ถ๐ผ๐ป.
๐ช๐ต๐ฎ๐ ๐๐ณ ๐๐ต๐ฒ ๐๐ถ๐ฟ๐๐ ๐ฅ๐ฒ๐พ๐๐ฒ๐๐ ๐๐ฎ๐ถ๐น๐?
We also need to think about failures.
Suppose a request creates an idempotency record but the actual operation fails.
Request
โ
Store key
โ
Operation fails
Should the same key be allowed to retry?
The answer depends on the system's business rules and how the idempotency record is designed.
Some systems store the final failure and return the same result. Others allow the operation to be retried when the failure is considered safe to retry.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐๐ต๐ฒ๐ฟ๐ฒ๐ณ๐ผ๐ฟ๐ฒ ๐ฟ๐ฒ๐พ๐๐ถ๐ฟ๐ฒ๐ ๐ฎ ๐ฐ๐น๐ฒ๐ฎ๐ฟ ๐ฑ๐ฒ๐ฐ๐ถ๐๐ถ๐ผ๐ป ๐ฎ๐ฏ๐ผ๐๐ ๐ฏ๐ผ๐๐ต ๐๐๐ฐ๐ฐ๐ฒ๐๐๐ณ๐๐น ๐ฎ๐ป๐ฑ ๐ณ๐ฎ๐ถ๐น๐ฒ๐ฑ ๐ฎ๐๐๐ฒ๐บ๐ฝ๐๐.
๐๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐ ๐๐ ๐ฎ ๐๐๐๐ถ๐ป๐ฒ๐๐ ๐๐ฒ๐ฐ๐ถ๐๐ถ๐ผ๐ป, ๐ก๐ผ๐ ๐๐๐๐ ๐ฎ ๐ง๐ฒ๐ฐ๐ต๐ป๐ถ๐ฐ๐ฎ๐น ๐๐ฒ๐ฎ๐๐๐ฟ๐ฒ
There is no single idempotency strategy that works for every operation.
Consider:
* Create Payment
* Create Order
* Send Email
* Submit Job
* Update Profile
Each operation has ๐ฑ๐ถ๐ณ๐ณ๐ฒ๐ฟ๐ฒ๐ป๐ ๐ฏ๐๐๐ถ๐ป๐ฒ๐๐ ๐ฟ๐๐น๐ฒ๐ and different consequences when repeated.
Idempotency is not simply a technology that we add to every API. ๐๐ ๐ถ๐ ๐ฎ ๐ฏ๐๐๐ถ๐ป๐ฒ๐๐ ๐ฎ๐ป๐ฑ ๐๐๐๐๐ฒ๐บ-๐ฑ๐ฒ๐๐ถ๐ด๐ป ๐ฑ๐ฒ๐ฐ๐ถ๐๐ถ๐ผ๐ป.
Before implementing idempotency, an engineer should understand:
๐ช๐ต๐ฎ๐ ๐ถ๐ ๐๐ต๐ฒ ๐ผ๐ฝ๐ฒ๐ฟ๐ฎ๐๐ถ๐ผ๐ป'๐ ๐ฏ๐๐๐ถ๐ป๐ฒ๐๐ ๐ฒ๐ณ๐ณ๐ฒ๐ฐ๐, ๐ฎ๐ป๐ฑ ๐๐ต๐ฎ๐ ๐๐ต๐ผ๐๐น๐ฑ ๐ต๐ฎ๐ฝ๐ฝ๐ฒ๐ป ๐ถ๐ณ ๐๐ต๐ฒ ๐๐ฎ๐บ๐ฒ ๐ฟ๐ฒ๐พ๐๐ฒ๐๐ ๐ฎ๐ฟ๐ฟ๐ถ๐๐ฒ๐ ๐ฎ๐ด๐ฎ๐ถ๐ป?
Good system design starts with that questionโnot with choosing a database column or library.
๐ง๐ต๐ฒ ๐๐ถ๐ด๐ด๐ฒ๐ฟ ๐๐ฒ๐๐๐ผ๐ป
Idempotency looks like a small API feature, but it teaches a much bigger software-engineering lesson.
Distributed systems are full of uncertainty:
Requests can repeat.
Responses can disappear.
Workers can retry.
Messages can arrive again.
Multiple requests can run together.
We cannot always prevent these situations.
What we can do is design our systems so that these situations do not create incorrect results.
๐ง๐ต๐ฎ๐'๐ ๐๐ต๐ฒ ๐ฟ๐ฒ๐ฎ๐น ๐ฝ๐๐ฟ๐ฝ๐ผ๐๐ฒ ๐ผ๐ณ ๐ถ๐ฑ๐ฒ๐บ๐ฝ๐ผ๐๐ฒ๐ป๐ฐ๐: ๐ป๐ผ๐ ๐ฝ๐ฟ๐ฒ๐๐ฒ๐ป๐๐ถ๐ป๐ด ๐ฟ๐ฒ๐ฝ๐ฒ๐๐ถ๐๐ถ๐ผ๐ป, ๐ฏ๐๐ ๐บ๐ฎ๐ธ๐ถ๐ป๐ด ๐ฟ๐ฒ๐ฝ๐ฒ๐๐ถ๐๐ถ๐ผ๐ป ๐๐ฎ๐ณ๐ฒ.
๐๐ถ๐ป๐ฎ๐น ๐ง๐ต๐ผ๐๐ด๐ต๐๐
Idempotency taught me an important lesson about building reliable software:
๐ช๐ฒ ๐ฐ๐ฎ๐ป๐ป๐ผ๐ ๐ฎ๐น๐๐ฎ๐๐ ๐ฝ๐ฟ๐ฒ๐๐ฒ๐ป๐ ๐ณ๐ฎ๐ถ๐น๐๐ฟ๐ฒ๐, ๐ฟ๐ฒ๐๐ฟ๐ถ๐ฒ๐, ๐ผ๐ฟ ๐ฑ๐๐ฝ๐น๐ถ๐ฐ๐ฎ๐๐ฒ ๐ฟ๐ฒ๐พ๐๐ฒ๐๐๐โ๐ฏ๐๐ ๐๐ฒ ๐ฐ๐ฎ๐ป ๐ฑ๐ฒ๐๐ถ๐ด๐ป ๐ผ๐๐ฟ ๐๐๐๐๐ฒ๐บ๐ ๐๐ผ ๐ต๐ฎ๐ป๐ฑ๐น๐ฒ ๐๐ต๐ฒ๐บ ๐๐ฎ๐ณ๐ฒ๐น๐.
The next time you design an API, background job, payment flow, or distributed service, ask one simple question:
"๐ช๐ต๐ฎ๐ ๐ต๐ฎ๐ฝ๐ฝ๐ฒ๐ป๐ ๐ถ๐ณ ๐๐ต๐ถ๐ ๐ผ๐ฝ๐ฒ๐ฟ๐ฎ๐๐ถ๐ผ๐ป ๐ฟ๐๐ป๐ ๐ฎ๐ด๐ฎ๐ถ๐ป?"
That question can reveal problems that are easy to miss during normal development.
Thanks for reading! ๐
I hope this article helped make idempotency easier to understand, especially if you're learning distributed systems or building production-level applications.
If you found it useful, feel free to share it with another developer who might benefit from it.
Keep building. Keep learning. Keep questioning how your systems behave when things go wrong. ๐
๐๐ฏ๐ผ๐๐ ๐๐ต๐ฒ ๐๐๐๐ต๐ผ๐ฟ
I'm a software engineer interested in building reliable software systems and continuously improving my understanding of system design, distributed systems, and backend engineering.
This article was written while exploring these concepts through hands-on project development and practical engineering problems.




Top comments (0)