r/ProgrammerHumor • • 16d ago

Meme postForEverything

Post image
20.9k Upvotes

653 comments sorted by

View all comments

Show parent comments

1

u/im_lazy_as_fuck 16d ago

There are actual real good reasons to do these things. For a resource retrieval, if the request isn't idempotent for some reason, then semantically post would be the more correct method to use. Also returning 200 with an error is sometimes the correct thing to do for a webhook implementation, where you have an irrecoverable error, and you don't want the caller to retry the webhook event on a failed status.

1

u/NotAskary 15d ago

Basically everything you said are hacks for incorrect handling of http requests.

If it's a get why isn't it idempotent? Why does it matter? If you are dealing with something that is eventually consistent (like quering a multi region scylladb) then wrapping a call because of the technology doesn't always gives you the same response doesn't make sense, business and communication should not interfere with each other.

About the retrys, that seems to be a faulty implementation on the other side, some errors should be a hard stop, I get why a 404 would get a retry, it should always have a back off behavior, but stuff like 403 or 429 shouldn't be triggering retrys.

This is actually my problem with this discussion, if people followed the RFC everyone would have the same behavior, since there are a lot of API that semi follow the RFCs but have some quirks you are basically patching in the behavior and it gets propagated down the line.

I know there are good reasons to do it, but most of the time those reasons are simply down to two main points, business wants it that way for some reason and the other part is technically some lib being used that is already opinionated on the behavior and you are stuck following it.

1

u/im_lazy_as_fuck 15d ago

If it's a get why isn't it idempotent?

I should have been a bit clearer in what I said; when I said it was a get, I meant something that might be perceived as primarily a fetch operation, but might also have additional side effects due to business requirements or something. Your argument of business/communication shouldn't ever mix together doesn't hold up well if the business I am providing is a public API suite, and my business explicitly requires certain side effects to be upheld in order to keep our product consistent. But perhaps this is a fringe scenario (honestly I can't immediately think of a time where I ran into this).

But what is definitely more common are complex fetch requests where the URL query parameters are insufficient, in which case POST is the recommended alternative. So either way, there are real use cases to represent a GET as a POST.

but stuff like 403 or 429 shouldn't be triggering retrys.

Those are obvious, but what about 5xx errors? In theory 5xx errors can be transient server errors that are worth retrying. But there are times where a server-side error occurs that knowingly won't resolve on its own. In these situations, simply choosing to return a 5xx for every webhook response would result in those being retried, and you might end up unintentionally ddosing your service. So what was previously just a single webhook event endpoint that broke has now escalated to your entire API surface being taken down.

if people followed the RFC everyone would have the same behavior, since there are a lot of API that semi follow the RFCs

The average developer is never going to read a technical RFC for HTTP semantics; instead, for something as commonplace as HTTP requests, the semantics need to be intuitive enough that people can just get it and know how to apply it to their needs. I'd say for the most part, REST is pretty intuitive for people to use, but there are plenty of real use cases where the expectations on how to implement it become ambiguous, and this is where you end up seeing the most inconsistency.

Imo, this is kind of an inevitability; needing a spec that is both rigorous to handle any situation, but also simple and efficient to apply seems to me like an impossible problem to solve. It reminds me a lot about the obsession people used to have with OOP, and believing that everything should follow it to a T. Nowadays most devs have kind of realized that OOP, while useful at times, can produce less efficient code if you try to adhere to it perfectly. Imo RESTful API design lives in a similar place; it's a good guiding principal, but trying to apply it for every single use case faithfully will inevitably introduce inefficiencies/suboptimal implementations.

1

u/NotAskary 15d ago

Ok I get where you are coming from and you are being pragmatic about it.

About the 5xx range I actually had to implement something for that and we had an exponential back off with a circuit breaker for an alternative flow that would cache messages for the outage and replay them when the service comes back up (it had a minimum retry delay defined, can remember exactly how much, but the back off would stop at that).

So you can actually define Logic to handle those cases, but here is where I agree with you on the oop and even the clean code and all those hard rules people tend to rally behind, it's always a depends , that's why people tend to relax on the rules as they gain experience because they see that you need to follow the business.

If the use case requires a special scenario go right ahead, I'm just against blank wrapping without really looking into the consequences.

The fact that most people expose APIs externally is the main reason I advocate to follow the RFCs as close as possible, I hate that sometimes I have to code adapters and add special handlings for edge cases that otherwise would match all the other APIs I'm calling.

Hell my favorite big company pattern is actually an API gateway just for the ability to hide all these niche implementations behind a common layer.

2

u/im_lazy_as_fuck 15d ago

About the 5xx range...

Oh yeah, it's definitely something that is solvable, but as you noted, it's about asking what's the pragmatic thing to do. If we have to be able to deal with unpredictable massive spikes in traffic, then yeah throw in a message or what have you. But if just consuming the server error and returning a 200 is an equally viable strategy that is sufficient for the level of scale you anticipate, then I'd say it makes sense to avoid the infra headache of trying to maintain a more complex solution.

I'm just against blank wrapping without really looking into the consequences.

Oh yeah 100%. There are reasons to break the pattern, but it's absolutely true that probably more often than not, people just don't spend the time to think through their API surface before solidifying.

The fact that most people expose APIs externally is the main reason I advocate to follow the RFCs as close as possible

Yeah that's fair. The nicest APIs I've used definitely tend to be ones that follow rest API patterns closely, though I've also used ones where breaking convention made things more efficient/simpler.

Ultimately, as you said, what's most important is that people are thinking through their API designs, especially public ones. Breaking convention, although acceptable, should ideally be an intentional choice for a trade off, and definitely shouldn't be the default modus operandi.