Build1 distinct publisher3 min readPublished
A client header only gives an operation a stable name. Honestly replaying a response needs a SHA-256 fingerprint of six request fields, a lock every worker shares, and a record that survives the crash the retry is answering.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Read the naive version as an ordering problem rather than a caching problem. Check the store for the key, and if it is empty run the controller, save the response under the key, then return it [5]. Two gaps sit in that order. The first is between the read and the controller call, which is the case the article demonstrates with two workers that both read no record and both execute [9]. The second is between the controller returning and the save landing, and a lock does nothing for that one [3].
The second gap is a transaction-boundary question. Shared idempotency state plus Symfony Lock closes the race [10], but the record saying "we already did this" is a different write to a different system than the order row. If business data commits to a database and the idempotency record goes to a cache, no single commit covers both. Die in between and you have order #742 with nothing to replay [3], which is the exact state the key was bought to prevent. For the guarantee to hold end to end, the record has to be written inside the same transaction as the business effect, or reserved before execution and reconciled afterwards. Neither is free, and which of the two a bundle assumes is the first thing I would go looking for in its docs.
Now the fingerprint. Six fields go in: HTTP method, logical operation, path, normalized query parameters, normalized Content-Type and the raw request body, hashed to SHA-256 [6][1]. Query parameters and Content-Type get normalized, but the body stays raw [6], which means byte equality, not semantic equality. A retry that re-serialises the same object with map keys in a different order, or a client that stamps a fresh trace id into the payload, produces a different digest and collects a 422 [7]. The real question to answer before adopting this isn't about the hash function at all: it's whether your HTTP client buffers the exact bytes it sent the first time and replays those, or rebuilds the body from an object on each attempt.
The 422 is the part of the design I would defend. The author's stated line is that idempotency should protect against duplicate execution, not hide application bugs [8], and returning the first response to a second, different payload under the same key would be worse than plainly failing [14]. Handing a client a 422 at 3am is unpleasant, but it beats handing it someone else's order id.
What a server-side store cannot cover is anything the server already said to a third party. The article's own list of work that may already have completed before the response was lost includes a call to a payment provider [2]. That call needs its own key at the provider, and a replayed local response says nothing about whether the provider deduplicated. The same holds for a message already handed to a broker.
The writeup promises to name what cannot be guaranteed [12]. The text supplied to me breaks off mid-sentence while making the point that local state stops working once you run multiple PHP workers, containers or application nodes [11], so that list is not in front of me and I am not going to guess at it. Everything above the break is sound: the fingerprint decision is the right one, and the choice to fail loudly on key collision is better engineering than a silent replay.
Ranked by verification strength, evidence, and original report placement.
The bundle uses two separate pieces, shared idempotency state and Symfony Lock, and both must really be shared across the application.
The article says the shared-state distinction matters in production if you run multiple PHP workers, containers or application nodes; the supplied text breaks off mid-sentence at that point.
The author of the dev.to piece built HttpIdempotencyBundle, described as a small Symfony bundle for explicit HTTP request idempotency, and says the interesting part is not the bundle itself.
The article frames the risk as an application that may already have created the order, reserved the stock, sent the message or called a payment provider, with only the response lost; from the client's point of view the request failed.
The worked sequence: client sends POST /orders, the server creates order #742 and sends a 201, the connection dies, the client sees a timeout and retries, and unless another mechanism is in place order #743 may also be created.
The article states that transport failure and business-operation failure are not the same thing, and that HTTP cannot always tell the client whether the operation happened.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 30, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Once the question needs a cube, you own the parser1 distinct publisher
build
Thirty lines of Doctrine filter, and the query paths where it is simply not there1 distinct publisher
build
Disabling Submit Fixes the Click, Not the Lost Response1 distinct publisher
build
Two layers instead of an outbox: when the dual-write gap is cheap enough to live with1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Reasoning you can check, behaviour you cannot
Split the claims in two and the picture is clear. The failure modes — retry after a lost 201, two workers both reading no record, a stale pre-lock read — are argued with sequences a reader can verify by inspection, and they hold up. The other half, what the bundle itself does, is asserted by its maintainer: six fingerprint fields, SHA-256, 422 on mismatch, 409 on lock contention, all unaccompanied by a repository reference, version, test or third-party confirmation in our coverage. The text also stops mid-sentence, taking part of the second-read explanation with it.
No trace beyond the author's post
Not a single install figure, dependent project, production deployment or named user appears anywhere in this reporting — only the maintainer describing his own bundle on dev.to. Absence of numbers is not evidence of low uptake, so we score nothing here rather than guess.
Sells itself short, then leaves a door open
This runs the opposite way from most library announcements. The author says up front that the bundle is not the interesting part, spends the piece on what cannot be guaranteed, and chooses a 422 over a convenient silent replay — refusing the exact shortcut that would make a demo look smoother. The pull back toward zero is the window the sequence itself exposes: execution precedes the store, and nothing in the supplied text says what a retry receives if the process dies in between.
Maintainer describing his own work, openly
The one account we have is written by the person whose bundle is being explained, on a platform where a well-argued post is how a small package finds users. That interest is disclosed in the first person rather than dressed as neutral reporting, and no sponsor, employer or commercial offering is involved — but every runtime claim about HttpIdempotencyBundle still comes from the party who benefits when readers adopt it.
Confident about the problem, provisional about the tool
We would stand behind the diagnosis — reused keys need request fingerprints, and a lookup without a shared lock and a second read will double-execute — because the reasoning is self-supporting. We would not yet stand behind a description of how this particular bundle behaves under load, on a truncated single-source post with no adoption signal and no independent look at the code.