Hacker News new | ask | show | jobs
by xlii 47 days ago
Don't fix other people problems.

If idempotent key was seen then send back response.

Clients intention is outside the scope. If contract says "idempotency on key" the idempotent response on key. If contract says "idempotent on body hash" then response on body hash (which might or might not include extra data).

APIs are contracts. Not the pinky promise of "I'll do my best guess"

7 comments

IMHO it's more: fix problems, or at least mitigate them, regardless whose problem it is.

I've been in this situation, a clientside bug meant that different requests arrived with the same idempotency key.

In my case, updating the client would have taken weeks, in the best case scenario. Updating the backend to check for a matching request body would have taken minutes, maybe hours.

It took me a surprising amount of arguing to convince people that, even if it was a clientside bug, we couldn't let users suffer for weeks in name of "correctness".

Yeah but don't let them reify it.

Ideally you already send client version in requests (or have an API version prefix). Add the workaround only for legacy clients.

Next client version must distinguish itself from predecessor and must not require the bodge to work.

Well.. it was ~6 years and ~10 billion payments ago, the clients have been fixed but the "hack" is still there, it has caused no harm as far as I can tell. Worst case scenario it's useless, best case scenario it prevents regressions.

The issue with things that client must not do is that they might still do them, and users don't care whose fault it is. It's important to have auxilliary mechanisms to mitigate these.

That it may be there or not doesn't mean it "caused no harm". It sounds like yet another carbuncle added in haste and then never fixed properly, leading to 6 years of fear of touching it.

If it's truly intended, it needs to be part of the official spec, with a robust justification of why it's there at all. Neither server nor client ought to have unnecessary and undocumented things "just in case", because that breeds a culture of uncertainty.

If you fear client regressions, make it a mandatory part of the client's test suite. You control the client, right?

> In my case, updating the client would have taken weeks, in the best case scenario. Updating the backend to check for a matching request body would have taken minutes, maybe hours.

Then at least admit you’re just hacking quickly fixes, creating technical debt, and not fixing the actual problem.

I agree with your point that business interest is most important, I disagree that it’s the technically most appropriate solution.

The whole article is proclaiming that this is a technical problem about idempotency being hard, while it’s not. The whole premise of client side bugs must be resolved backend side as the correct solution is incorrect.

Eh, idk, I wouldn't classify these fixes as hacks nor as technical debt. It's labels that only work from a partial perspective. IMHO a solution that expects perfect compliance is not really complete, it's not good enough to put all the burden on the client, idempotency keys are part of the solution, but not the solution. So, in this sense I would say it is a technical problem.
> APIs are contracts. Not the pinky promise of "I'll do my best guess"

You have never had to work with PHP backends, have you?

JSON in PHP is a flustercluck. Undefined, null, "" or "null", that is always the question.

If you use a typed Go/Rust client and schemas, you usually end up with "look ahead schemas" that try to detect the actual types behind the scenes, either with custom marshallers or with some v1/v2/v3 etc schema structs.

It's so painful to deal with ducktyped languages ... that's something I wouldn't wish on anyone.

Yes I have. You learn the quirks and then it’s ok.
I would rather do more work myself to make an API as fool proof as possible, than hope everyone else is perfect, and lose data when they're not.
That just leads to bigger fools. I don't just mean that as clever wordplay, but as a serious point. No matter how sloppy you make your API someone else will use it even more sloppily. Now you've got an enormous sloppy surface you can't properly contain or maintain, and people still transgress its boundaries even so.

The robustness principle has its times and places but the general consensus that it should be applied everywhere to everything was a big mistake. The default should be that you are very rigid and precise and only apply the robustness principle in those times and places it applies, and I'm perfectly comfortable waiting to deploy something precise and find out that this was one of them. The vast majority of APIs is not the time and place for the robustness principle. It's the time and place for careful precision on exactly what is provided, and detailed and description error messages, logging, and metrics for when the boundaries are transgressed.

Sometimes "best guess" is the contract. Obviously many applications can't tolerate that, but surprisingly many can.

The user just needs to know what the trade-off is. And "best guess" can be hard to characterize, so you need to be extremely careful. But sometimes it's a big win for a low price.

The best pattern I've seen is hash the request and validate that it is the same one. If not, error out as it is an invalid argument.

"Best guess" can be bad if it is not well-defined, but you can still make error detection obvious rather than hidden.

APIs always have unspecified edge cases (for any sufficiently complex API). In those cases, the API usually does the author's best guess of the proper behavior.
In this space the "client" may be a hardware terminal with firmware that's difficult if not impossible to update.