Live data from Hacker News

APIs, robustness, and idempotency

stripe.com

31–40 of 52 posts

Re: APIs, robustness, and idempotency

#31
post #7

Earlier quoted context omitted.

(I work at Stripe.) API versioning is definitely a debatable subject, and I don't think that anyone at Stripe would claim that the current state of affairs is perfect by any means, but it's one that we think provides a good compromise between the stability of client integrations and our own ability to iterate on the API's design and make progress. The classic problem with web APIs is that unless you have a good versi…

If you don't mind me asking how exactly to you guys process requests with a versioned API? Say I come in with a request for V2. How does that get directed to the V2 code path? What about services that are identical in V1 and V2. Do you have 2 copies of the same logic? Sorry for the naive question but API versioning is something that has been on my mind recently.

> If you don't mind me asking how exactly to you guys process requests with a versioned API?

This information has been talked about publicly before, so I don't mind explaining at all.

For the most part, the core API endpoint logic is all coupled to just the latest version. For each substantial API change in each new API version, logic is encapsulated into what we call a "compatibility gate".

Before responding, a merchant's current version is looked up, and the response is passed back through a compatibility layer that applies changes for each gate until it's been walked all the way back to the target version, then the response is sent back.

I'm glossing over a few details here of course — versioning can affect request parameters and even core logic in many places, so some gates need to be embedded throughout core code. We try to keep that as clean as we can.

Re: APIs, robustness, and idempotency

#32
post #28
post #26

Earlier quoted context omitted.

Is there any plan for the ability to discard previously used idempotency keys, so that a request with all the same parameters can actually be duplicated? Sometimes a payment will fail because a customer's card is declined (e.g. because of fraud detection or insufficient funds), and after the customer sorts it out, we'd like to try recharging their card, and not just receive the same error message. Our current workaro…

This is by design. If you want to make "another attempt", you should use a new idempotency key. Think of it as "one attempt/transaction/request" == "one idempotency key".

Yes, exactly! Like you say, the idea is that an idempotency key represents a single request. It's perfectly okay to make a new request that's almost identical to the original in that it shares the same endpoint and all the same parameters, but that should be done using a freshly generated idempotency key.

Re: APIs, robustness, and idempotency

#33
post #9

I authored this article and just wanted to leave a quick note on here that I'm more than happy to answer any questions, or debate/discuss the finer points of HTTP and API semantics ;) An ex-colleague pointed out to me on Twitter today that there are other APIs out there that have developed a concept similar to Stripe's `Idempotency-Key` header, the "client tokens" used in EC2's API for example [1]. To my knowledge th…

My team and I would love a blog post from you guys about the architecture of your idempotency tokens!

Re: APIs, robustness, and idempotency

#34
post #3

One thing about Stripe's API I have mixed feelings about is the liberal versioning. My experiencing with 100s of payment integrations is that they get done once and hopefully never touched again. I know most of Stripes updates are "additive" such that they are backwards compatible if coded liberally, but it can be confusing. Same with Lob.

My experiencing with 100s of payment integrations is that they get done once and hopefully never touched again. This is basically the only way to work with any payment service integration, in my experience. A lot of these services concentrate on documenting their current versions but not historical ones, so maintenance of integrations using older versions is unnecessarily difficult, even if you only want to use other…

It sounds like you haven't looked a bit at Stripe's versioning, given your generic critiques.

Stripe's API versions come with absurdly detailed changelogs, documentation that's complete for each version and automatically shows you the right doc for the version you're on, and consistency/forethought in API design that means very, very rare breaking changes that cause real problems for an application.

I've used them for almost 3 years now in an extremely complex payments system -- we use nearly every feature they offer in several different ways -- and it's always been a pleasure.

Re: APIs, robustness, and idempotency

#35
post #9

I authored this article and just wanted to leave a quick note on here that I'm more than happy to answer any questions, or debate/discuss the finer points of HTTP and API semantics ;) An ex-colleague pointed out to me on Twitter today that there are other APIs out there that have developed a concept similar to Stripe's `Idempotency-Key` header, the "client tokens" used in EC2's API for example [1]. To my knowledge th…

Hi. Thanks for the very interesting article. Quick question regarding the following:

> "On a response failure (i.e. the operation executed successfully, but the client couldn’t get the result), the server simply replies with a cached result of the successful operation."

Have you considered having the server respond with different http-status-codes for the initial successful request as opposed to any nop-retries? This provides the client with additional information that may prove useful. A lazy client could simply choose to treat both response codes identically, as a success indicator. And a more diligent/sophisticated/paranoid client could choose to act upon this information in some other way.

From a side-effects/persisted-data perspective, returning a different status-code would have the exact same effects as what you described. But it would also give clients additional information that they can choose to act upon if desired.

Re: APIs, robustness, and idempotency

#36
post #30

Earlier quoted context omitted.

As the author of a http library (lua-http https://github.com/daurnimator/lua-http ) that doesn't, I'm interested in how you'd want retries (not to mention exponential backoffs) to work: - Should they be the default? - What requests should be retried? (as much as we wish GETs were idempotent... they're not) see https://lists.w3.org/Archives/Public/ietf-http-wg/2017JanMar... for an intro to the complexities here - What…

That list of questions is why I prefer retry and back-off to be separate from the internals of the http library. As a library user I need to work around the peculiarities of the particular service endpoints I'm integrating with. Backoff and retry aren't specific to the application/transport protocol through which a service is consumed. For example, (in the java world) the approach in libraries like hysterix, guava-re…

What do you recommend for JS?

Re: APIs, robustness, and idempotency

#37
post #35
post #9

I authored this article and just wanted to leave a quick note on here that I'm more than happy to answer any questions, or debate/discuss the finer points of HTTP and API semantics ;) An ex-colleague pointed out to me on Twitter today that there are other APIs out there that have developed a concept similar to Stripe's `Idempotency-Key` header, the "client tokens" used in EC2's API for example [1]. To my knowledge th…

Hi. Thanks for the very interesting article. Quick question regarding the following: > "On a response failure (i.e. the operation executed successfully, but the client couldn’t get the result), the server simply replies with a cached result of the successful operation." Have you considered having the server respond with different http-status-codes for the initial successful request as opposed to any nop-retries? This…

How would the client act differently upon it? Sounds about as useful as telling the client how many TCP packets in the connection had to be retransmitted by the server.

Re: APIs, robustness, and idempotency

#38
If your requests are "POST to create something" requests, you can get a more REST-ful flavor of idempotency by turning the POST into a redirecting GET followed by a PUT to emulate two phase commits.

Instead of POSTing to /transactions, I GET /transactions/fresh (optionally a URL linked from /transactions to avoid assumptions about URL structure and capabilities) which generates a unique ID and redirects to /transactions/{some-unique-id}. Attempting to GET that transaction would return 404 as it doesn't exist yet, but I can PUT to it to write a transaction. Now I'm using only idempotent methods instead of POST, and I don't need to figure out how to properly construct and manipulate a token in a header, and proxies don't need to know about this special header to know requests are idempotent since the methods I'm using communicate that already.

This adds a minimum of one extra request to all two-phase resource creations. If your goal is to retry safely though, the number of retries could dwarf that overhead. It all depends on how likely it is that failures actually occur. Client-side ID generation removes the extra request but brings back the problems of clients needing to understand URL and ID formats and construction logic.

Re: APIs, robustness, and idempotency

#40

Earlier quoted context omitted.

My experiencing with 100s of payment integrations is that they get done once and hopefully never touched again. This is basically the only way to work with any payment service integration, in my experience. A lot of these services concentrate on documenting their current versions but not historical ones, so maintenance of integrations using older versions is unnecessarily difficult, even if you only want to use other…

It sounds like you haven't looked a bit at Stripe's versioning, given your generic critiques. Stripe's API versions come with absurdly detailed changelogs, documentation that's complete for each version and automatically shows you the right doc for the version you're on, and consistency/forethought in API design that means very, very rare breaking changes that cause real problems for an application. I've used them fo…

It sounds like you haven't looked a bit at Stripe's versioning, given your generic critiques.

I was being polite. Stripe is my go-to example for integrations with payment services sometimes becoming write-only by default as time passes and their API moves on.

Stripe's API versions come with absurdly detailed changelogs

Stripe's API has a basic changelog. This is helpful, but also the minimal requirement to be useful at all.

It would be more useful to provide a migration guide that also shows how to change an existing integration to work with newer API versions when they make significant changes, similar to the documentation available for setting up a new integration in the first place.

documentation that's complete for each version

As Stripe's service has grown, unfortunately their documentation has grown less reliable. There have been quite a few errors and omissions in recent years, which we've pointed out to them from time to time.

As for "each version", where have you found official documentation for any API version except the latest?

automatically shows you the right doc for the version you're on

No, it doesn't. I've just checked this.

consistency/forethought in API design that means very, very rare breaking changes that cause real problems for an application

Breaking changes aren't particularly unusual with Stripe. Fortunately, they are pretty reliable at supporting older API versions as well, which is to their credit.

Again, I would consider this a minimal requirement to be useful in this industry, but it's not something that everyone does as well as they do and I know it's part of the reason some businesses stay with them.

I've used them for almost 3 years now in an extremely complex payments system -- we use nearly every feature they offer in several different ways -- and it's always been a pleasure.

Unfortunately that doesn't mean the limitations of how their system works are any less limiting.

In addition to the various points about, I'd add that although you can override your current API version on a request, you can't similarly override it temporarily on webhooks, so testing an updated integration is difficult.

There's also, as far as I've ever seen, no documentation that specifies exactly what the effects of that API version setting on the dashboard are. For example, will changing it affect both production and testing environments simultaneously, and can it be downgraded again when testing against a certain API version is finished or if a regression is found? You can only find these things out by trial and error in my experience, and it's a brave developer who'd try that on their first integration when it might compromise their production system.

It's still not possible to set up a fully automated integration test suite either. This would obviously be a very useful facility if you wanted to check whether your integration would still work properly against a new API version.

I'm happy that you're happy, but please consider that there are plenty of us who have also been working with Stripe and maintaining integrations with their systems for a lot longer than 3 years, and maybe we've simply encountered different problems than you have.

[Edit: Removed some unnecessary snark. The point of this comment was not to have a dig, because Stripe do better than many in this area. But the problem of effectively becoming version locked as a current API gets further ahead of the current integration is ubiquitous with web services, including payment services. Almost all of them, at least among the ones I've used professionally, could do more to help their users not just integrate initially but also maintain or update those integrations, possibly months or years after they were first written.]

Post reply on HN