Idempotency - What is it and why does it matter?

The short version

Some of our most frequently visited technical articles reference Idempotency, but what exactly is it and why are engineers interested in achieving it?

In any system where services send messages to each other, some messages will arrive more than once.  That is not a bug per se, but a property of how reliable distributed systems work.  Achieving idempotency is the design discipline that makes a duplicate harmless, so an account is credited only once, and a customer notification is sent only once, no matter how many times the underlying message turns up.

What idempotency means

An action is idempotent if doing it twice has exactly the same result as doing it once.

Think of a lift call button.  Pressing it five times still brings one lift.  Compare that with a vending machine: pressing “buy” five times takes your money five times.  Idempotent systems behave like the lift button.

In software, the instruction “set this customer’s address to 10 High Street” is naturally idempotent.  The instruction “pay this supplier £10,000” is not: run it twice and the money leaves twice.  Most valuable business actions fall into the second group, which is why idempotency is a deliberate design decision.

Why duplicates are inevitable between services

Modern platforms are built from many independent services that pass messages to each other, often through a messaging platform such as Kafka.  It’s typical that such platforms are designed with the constraint of never losing a message.  But nothing is free, and the price of “never lose” is “occasionally deliver twice”.

Duplicates arise in ordinary, everyday situations:

  • A sender is unsure its message arrived.  A brief network blip means no confirmation comes back, so it sensibly sends again.
  • A receiver is slow.  If a service takes too long, the platform assumes it has failed and hands the same work to another copy of the service.  Potentially two are now processing the same job.
  • A service restarts mid-task.  Work that was partly done gets picked up again from the start.

None of these are rare edge cases.  At scale they happen daily, and the busier the system, the more often they occur.

Under strain, duplicates become near-certain.  Idempotency proves its worth when a system is under more pressure than usual: recovering from an outage, restarting services, or catching up on a backlog.  At those moments large volumes of work are retried or replayed at once, so duplicates are almost guaranteed.  Without idempotency, a recovery can turn one incident into thousands of double payments or repeat notifications, multiplying the cost of the original problem.  With it, services can be restarted and recovered at full speed, safely.

The business value

Without idempotency, a duplicate message becomes a duplicate business action.  With it, the duplicate is quietly detected and ignored.

[IMAGE PLACEHOLDER: replace this line with idempotency-table.png, the “Without idempotency / With idempotency” comparison table]

How to achieve idempotency, in plain terms

The recommended approach combines two well-established patterns.  Together they are the gold standard for handling duplicates.

  1. Give every message a unique reference.  Like an invoice number, it lets the receiving service tell a genuine new request from a repeat.
  2. Keep a ledger of what has been processed (the “idempotent consumer”).  Before acting, the service checks its ledger.  If the reference is already there, it skips the work.  If two copies arrive at the same moment, only one is allowed through.
  3. Record the work and the outgoing notification together (the “transactional outbox”).  The service saves its changes and any message it needs to send to other services in one single step.  Either both happen or neither does, so a failure halfway through can never leave things inconsistent or trigger duplicate messages further down the line.

The Cost?

These patterns add some design effort and extra moving parts.  However, they are essential where duplicates cost money or trust, such as payments, orders and customer communications.  For low-stakes data they may be more than is needed, so the decision should be made case by case.  No design removes every possible risk, so teams should still understand what a rare duplicate would mean for each process.

Questions to ask your teams

  • Which of our business actions would cause real harm if they happened twice?
  • For those actions, how do our services recognise and ignore a repeated message?
  • If a service fails halfway through a task, can it leave our data or partners in an inconsistent state?
  • Are our systems able to retry automatically and safely, or do we rely on manual clean-up?

Further reading

For the full technical detail, including implementation guidance and source code, see Lydtech Consulting’s Kafka Idempotent Consumer & Transactional Outbox article.

Don't miss these stories: