Use this when you need to teach one abstract concept concretely — pair a hands-on demo with a tiny glossary so an unfamiliar reader leaves with an intuition, not just a definition.
This is a copyable exemplar. Lift the #demo section into a real lesson built from assets/lesson-template.html — keep the design tokens and the Simple→Technical pattern verbatim.
Idempotent means an action is safe to repeat: doing it once, or doing it five times, lands you in the exact same place. The first try changes things; every retry after that quietly changes nothing.
This matters because the real world drops messages. A payment request times out, the network hiccups, a user double-taps "Pay." If your system isn't idempotent, every retry charges the card again. If it is, the customer pays once no matter how many times the request arrives.
Think of it like… a hotel elevator button. Press it once and the car comes. Press it nine more times while you wait — the elevator still comes exactly once. The extra presses don't summon nine elevators. An idempotent endpoint is that button.
The client generates a unique idempotency key (a UUID) once for the logical operation and resends it with every retry. The server records the key together with the result of the first successful run.
On a repeat request the server recognises the key, skips the side-effect entirely, and replays the stored response. The charge fires exactly once even though the HTTP request may arrive many times. Stripe, PayPal, and most payment APIs require an Idempotency-Key header for exactly this reason.
Note the difference from HTTP semantics: GET, PUT and DELETE are idempotent by definition; POST is not — so a "create charge" POST needs an explicit key to become safe to retry.
Two checkout systems, same card, same $42.00 order. Hammer the button and watch what each one does. Only one of them protects the customer.
Every request that arrives starts a brand-new charge. No memory of what already happened.
Every retry carries the same idempotency key. The first one charges; the rest replay that result.
Both sides receive the same request each time. The difference is entirely in how the server handles a repeat.
function charge(req) { // no key, no memory — always runs the side-effect payment.capture(req.card, 4200); return { status: "charged" }; }
const seen = new Map(); function charge(req) { const key = req.headers["Idempotency-Key"]; if (seen.has(key)) return seen.get(key); // replay const result = payment.capture(req.card, 4200); seen.set(key, result); // remember the outcome return result; }
The handful of terms used above, in plain words.
Idempotency-Key header in the keyed server's code.payment.capture() is the side-effect we want to fire exactly once.