It's interesting to compare and contrast this method of API management with Stripe.
As far as I understand, the Stripe api would continue to work indefinitely so long as you lock your api version, whereas Shopify would eventually break the app as they essentially backport breaking changes to older api versions.
Initially, I thought Stripe's method was superior, and would provide the best API experience, but realized that Stripe and Shopify have different incentives w/r/t their api.
For Stripe, breaking a functioning site harms revenue, and generally the developer is the Stripe customer.
For Shopify, their customer is the store owner, and for the most part, the store will continue to function because that is mostly controlled by shopify. The developer api is for added functionality, and it is in the interest of Shopify and the merchant that those apps continue to be updated and utilizing the latest features.
So, two different ways of managing breaking changes, but both are ultimately centered around providing the best customer experience.
>those apps continue to be updated and utilizing the latest features.
while this is orthogonal to Shopify API or your post at all, since you mentioned the need of updates, I just wanted to use that opportunity to vent my frustration with the constant push to update everything all the time and judging any piece of software by using "when was the last update" as a metric.
The problem I see is that not all apps or libs need (frequent) updates, many (maybe most) do need them, but some don't. They provide some functionality, they do that well and you could call them "complete". Maybe some security fix could be needed from time to time, but with a mature code being in use for many years event those are not frequent.
For example, consider something like ping utility. It does what it does for many decades. There was a need to add IPv6 support, but that was almost two decades ago. Why would anyone need to update it? I do not want any additional functionality, I don't want it to send emails or have social media share button. I want it to send ICMP echo requests and receive ICMP echo replies and nothing more. Aside from some security fixes no updates should be needed for 10+ years. This utility is done. It should not be thrown upon just because there were no updates for many years.
While of course neither ecommerce or Shopify platform are "done" and they get many updates now and will get updates in future it does not mean that some functionalities could have reached "done" stage.
For a "complete and done" addon, there could be a need for a security fix from time to time. There could be a need for some adjustments if a major browser introduces a new deviation from JS/CSS/HTML standards and forces everyone to update their code. But those events happen from time to time, possibly not that frequently. This means that some addon/plugin would not require any updates during the periods between those events and those periods could be many months/years long. But hey: "this addon did not receive any updates for 13 months, it must be really bad and should be avoided". This leads to a situation where a competing solution with tons of bugs will look better just because it receives two updates a week.
Funny that you mention Stripe, because it was definitely the canonical backwards compatibility API for me.
... Until just recently in mid Nov they changed some behavior that caused us to double/triple/quadruple charge customers unintentionally in some not-so-uncommon edge cases... I’m still trying to square this one with their support, so details are a bit thin. But definitely a big surprise for me to see this happening when previously I never imagined something like this can happen given the API versioning stability.
Glad to see that Shopify has better API versioning on their mind. When I used there API a few years ago, it was one of the worst APIs to depend on.
To the point, we had to architect our system to alert us for unannounced breaking API changes so we could fix and replay the JSON back.
- Moving JSON fields in and out of nestings didn't seem to be counted as a breaking change.
- Changes were rarely announced, and there was never a changelog as to what had changed (they look to have started one starting 2018 [1])
- When we contacted support about a brake, they would often be surprised.
- Often the only sign there would be a change would be that new fields would start to show up before a larger change.
All this would happen every few months. Reading this article I can start to see the reasons why this was happening.
I maintain a Shopify API package for .NET [0] and this has largely been my experience as well. Their attitude toward breaking API changes has caused me a good deal of frustration in the past. To make matters worse, their docs weren't (and still aren't, in my opinion) that great; my biggest complaint being they typically don't document when values can be null or even have a different type (e.g. property X could be a string or a decimal, but you'll never know looking at their docs).
This has led me to taking the drastic step of making _every_ property nullable. It's gross and feels bad to use, but at least it prevents JSON parse operations from crashing applications when a value is unexpectedly null.
To be honest, when I read about these types of difficulties in managing changes in an API over time, I really wonder why more companies don't go over whole hog into GraphQL. GraphQL won't solve all your problems (sometimes changes are breaking because business needs require it), but GraphQL provides a better scheme for API evolution than any other API toolkit I've used:
1. You can just keep adding new methods and fields as needed, but since each client asks only for the fields specific to what they want, you don't get big bloated response objects.
2. Lots of times your breaking changes only differ slightly from previous versions, and the way GraphQL resolvers are written makes it really easy to refactor things into one base method that both the old and new versions can share.
3. Proper use of the @deprecated schema directive means your doc is 'clean' by always showing the latest version that new users should adopt, but the doc is still there for users on older versions.
4. It's really easy to add logging and tracing in your resolvers to see how often fields are being accessed and who is using them. At some point you may decide to break backwards compatibility by deleting old fields, but you'll know exactly who you are breaking.
The Shopify developer experience is terrible. No fluffy blog can change that fact.
Shopify says the main product is the store owner. But the developers pick up all of the slack of Shopify.
Recurring payments. App.
Store backups. App.
Theme backups. App.
Order editing. Came late 2019.
Checkout. So locked down. Where’s the API?!
Slate tooling. Abandoned.
Starter themes. Abandoned.
Storefront SDK. Terrible documentation.
More than 1 variant image? App.
Metafields. App.
Wholesale. App.
Mailchimp. Removed.
People talk about google abandoning products. Shopify abandons nearly all developer tooling and is so locked down that it’s a constant “app for that” for the basics.
The interesting thing is theres been more than a few store owners I know that use Shopify. They’ve asked how to move off of Shopify.
I guess fulfilment is more important though. Right?!
Shopify's business plan is offer minimum functionality for a low monthly fee and then users use the marketplace to add additional functionality which Shopify takes a 20% cut.
I'm actually working on an open-source fulfillment and operations app for Shopify:
Shopify made a change to their API that was easily measurable on who it would affect, but didn't email us. Refused to grant us a temporary exemption (they would do it for $2000/m they said). The end result? They've sunk my business. I've replaced shopify now by writing my own but it's too late. My customers have all gone to my competitors and we're looking at pivoting.
What was the change? AFAIK Shopify would never give a temporary exemption for money, that's very much against their philosophy as a SaaS. Either you provide accurate details or this is just FUD.
Hi everyone. As the founder of an API company, this article has me wondering about the semantics of versioning — so I thought I’d ask the community a question. I hope the Shopify API team doesn’t mind!
When you all think about API versioning, what makes the most sense to you — a semver approach (major.minor.patch) a la NPM, or a date-based approach (2020-01-07) a la AWS? Or is some combination of the two desirable?
At Standard Library [0] we both allow people to publish APIs but also publish API proxies on behalf of partners (Stripe, Slack + others) using a semver approach. It’s not perfect but theoretically enforceable (schema parameter additions can be forced to require a minor update, schema parameter removals can be forced to require a major update). We’ve just stuck to this semver approach based on intuition and haven’t had negative feedback about it, but I do like the idea of time-based versioning.
Would love thoughts! If you want to play around you can build your own APIs using https://code.stdlib.com/, which uses the FunctionScript specification [1] to enforce HTTP request schemas.
I don't think there's one right answer, but my opinion is:
- Treat APIs as immutable.
- Any mutation results in a wholly new API version, not a patch or minor update.
- The developer will learn what changed, and how much changed by reading the patchnotes, not looking at which semver numbers changed. This is probably a healthy practice to encourage.
- Don't change APIs so much. The interface should be very carefully designed and tested and shipped like an NES cartridge: consider it impossible to fix once its shipped.
Therefore just start with `1` and increment each time.
The reason I don't like semver is that it's a developer convenience that leads to sloppy practices. You built your product against a specific API. If the API changes, you need to re-run your entire API evaluation, testing, blessing workflow. If the delta is tiny (what would have been a patch change) then yay, your task is likely going to be very simple. But you shouldn't see a bump version update and decide you can cut corners.
I prefer semver, but it's still not exactly what I want. What I'm looking for is the answer to 'how painful/risky is upgrading likely to be?'. I expect an x.x.1 release to be zero/low risk - small fixes only within current api and contract. I expect a 2.x.x release to come with major risk of breakage (and I'll almost always want to wait for 2.0.2-2.0.3 before actually upgrading - and I expect a 1.x.x -> 2.x.x release to have a risk of bigger changes compared to a 5.x.x -> 6.x.x since the latter is expected to have the fundamentals ironed out by now). But there doesn't seem to be much consensus on whether an x.1.x release can have breaking changes or not - or maybe they don't come with as strict a definition of a breaking change as Shopify is using - so I'm left with treating them the same as 2.x.x (though usually I don't wait for x.x.1 in this case).
I do not know any advantage of date-based versioning, other than someone knowing how new it is.
Semver is important for knowing whether or not to evaluate for breakages. You can theoretically combine both of these by making the date the patch version or supplying it as a version metadata
If you publish API version 1.0.0, and then internally you fix something and publish 1.0.1 (remember: a patch doesn't change the interface at all), should you continue to serve both the 1.0.0 and 1.0.1 APIs? How are they different from the consumer's perspective? What if your reason to release 1.0.1 is to fix a security issue - in what world is it ethical to continue to serve 1.0.0? If you can discontinue serving 1.0.0 at any time (because of a security patch released with 1.0.1), then you can't offer any long-term durability guarantees for early patch versions, and so indeed, offering older patch versions (when newer patches didn't fix security issues) is more likely to break your consumers when you're forced to discontinue older patch versions for security reasons, than if you refused to serve patch-level variants in the first place.
Because you, as upstream, have no control over whether the pure addition of fields will break downstream (since downstream may or may not be strict about what they accept), you should only offer semantically versioned minor releases if you're willing to guarantee durability for earlier minor versions, and can maintain multiple minor versions of your API in parallel. If not, then be explicit about the potential of your changes to break downstream - use timestamps and document sunset dates.
> When you all think about API versioning, what makes the most sense to you — a semver approach (major.minor.patch) a la NPM, or a date-based approach (2020-01-07) a la AWS? Or is some combination of the two desirable?
We're talking about HTTP APIs, right? Then neither. These are solutions with bad trade-offs for the problem at hand.
Version the link relations. This follows the principles that make the Web successful (a.k.a. REST). If that seems weird to you¹, consider the following:
You have a personal homepage type Web site. When you change or add or remove a document, do your users need to upgrade their user agent to keep using the site? Why not?
----
¹ Numerous developers are so enamoured with putting a version number into document URIs that they cannot fathom not doing it. This is another mutilation of the mind à la Dijkstra.
My impression is that semver makes far more sense for published libraries (DLLs or ruby gems or npm packages etc) while date-based makes far more sense for SaaS APIs. The constraints and usage patterns are fairly different between the two.
But with semver you can say: ok, I want every new version up to a new minor version (all fixes). While with a date based version you don't know how 'breaking' the changes will be.
Honestly, I don't see what a non-semantic scheme brings to the table. In SemVer, you can (theoretically) infer compatibility from the version number. A date tells you nothing.
(Disclosure: I work at Google on public APIs, opinions are my own)
Google's proposed a "stability" semantic as a third option[0]. TL;DR no breaking changes in the Stable channel but you can add backwards-compatible[1] features in-place.
A permanent Beta channel that's a superset of Stable lets users choose how change-tolerant they are. This lets API producers launch features earlier, knowing they will only impact risk tolerant users if breaking changes are needed. Theoretically this reduces the need for breaking changes in Stable, which require a new Major version.
I want to love Shopify but Shopify doesn't want to love developers. For example when their multi-location offering [0] _in beta_ they also announced that the Inventory API is going to breakingly change in 2 months and _none_ of their own SDKs supported locations at that point. This has happened with Shopify time and time again. Shopify doesn't manage API versioning or breaking changes: it just forces developers to update or endure with their broken applications and interfaces.
Sure, it's Shopify's choice. But considering how long their own changes take (for example multi-language is still in some sort of beta and it's been up and coming for like 5 years?) the API cycles are just brutal. And the saddest thing is that Shopify is still the best managed e-commerce platform for most usecases.
How do they handle security fixes in old releases? If releasing a security patch requires backporting it to 5 different active releases then I'm unconvinced that this is a useful strategy.
Great question! As mentioned in the article, when making a change you would typically add an `ApiChange.in_effect?` check to see if your new functionality should execute. When we implement security fixes, we do not include this check and the fix retroactively applies to all API versions.
At that point, I would use something like Gatsby which would build static pages for each of your product pages. You could host that on S3 with a CDN for cheap. You could downgrade the Shopify plan to $9/month since you're not going to be using their storefront.
[+] [-] Judson|6 years ago|reply
As far as I understand, the Stripe api would continue to work indefinitely so long as you lock your api version, whereas Shopify would eventually break the app as they essentially backport breaking changes to older api versions.
Initially, I thought Stripe's method was superior, and would provide the best API experience, but realized that Stripe and Shopify have different incentives w/r/t their api.
For Stripe, breaking a functioning site harms revenue, and generally the developer is the Stripe customer.
For Shopify, their customer is the store owner, and for the most part, the store will continue to function because that is mostly controlled by shopify. The developer api is for added functionality, and it is in the interest of Shopify and the merchant that those apps continue to be updated and utilizing the latest features.
So, two different ways of managing breaking changes, but both are ultimately centered around providing the best customer experience.
[+] [-] mr__y|6 years ago|reply
while this is orthogonal to Shopify API or your post at all, since you mentioned the need of updates, I just wanted to use that opportunity to vent my frustration with the constant push to update everything all the time and judging any piece of software by using "when was the last update" as a metric.
The problem I see is that not all apps or libs need (frequent) updates, many (maybe most) do need them, but some don't. They provide some functionality, they do that well and you could call them "complete". Maybe some security fix could be needed from time to time, but with a mature code being in use for many years event those are not frequent.
For example, consider something like ping utility. It does what it does for many decades. There was a need to add IPv6 support, but that was almost two decades ago. Why would anyone need to update it? I do not want any additional functionality, I don't want it to send emails or have social media share button. I want it to send ICMP echo requests and receive ICMP echo replies and nothing more. Aside from some security fixes no updates should be needed for 10+ years. This utility is done. It should not be thrown upon just because there were no updates for many years.
While of course neither ecommerce or Shopify platform are "done" and they get many updates now and will get updates in future it does not mean that some functionalities could have reached "done" stage.
For a "complete and done" addon, there could be a need for a security fix from time to time. There could be a need for some adjustments if a major browser introduces a new deviation from JS/CSS/HTML standards and forces everyone to update their code. But those events happen from time to time, possibly not that frequently. This means that some addon/plugin would not require any updates during the periods between those events and those periods could be many months/years long. But hey: "this addon did not receive any updates for 13 months, it must be really bad and should be avoided". This leads to a situation where a competing solution with tons of bugs will look better just because it receives two updates a week.
[+] [-] gingerlime|6 years ago|reply
... Until just recently in mid Nov they changed some behavior that caused us to double/triple/quadruple charge customers unintentionally in some not-so-uncommon edge cases... I’m still trying to square this one with their support, so details are a bit thin. But definitely a big surprise for me to see this happening when previously I never imagined something like this can happen given the API versioning stability.
[+] [-] circular_logic|6 years ago|reply
- Moving JSON fields in and out of nestings didn't seem to be counted as a breaking change.
- Changes were rarely announced, and there was never a changelog as to what had changed (they look to have started one starting 2018 [1])
- When we contacted support about a brake, they would often be surprised.
- Often the only sign there would be a change would be that new fields would start to show up before a larger change.
All this would happen every few months. Reading this article I can start to see the reasons why this was happening.
[1] https://developers.shopify.com/changelog?filter=all
[+] [-] nozzlegear|6 years ago|reply
This has led me to taking the drastic step of making _every_ property nullable. It's gross and feels bad to use, but at least it prevents JSON parse operations from crashing applications when a value is unexpectedly null.
[0]: https://github.com/nozzlegear/shopifysharp
[+] [-] hn_throwaway_99|6 years ago|reply
1. You can just keep adding new methods and fields as needed, but since each client asks only for the fields specific to what they want, you don't get big bloated response objects.
2. Lots of times your breaking changes only differ slightly from previous versions, and the way GraphQL resolvers are written makes it really easy to refactor things into one base method that both the old and new versions can share.
3. Proper use of the @deprecated schema directive means your doc is 'clean' by always showing the latest version that new users should adopt, but the doc is still there for users on older versions.
4. It's really easy to add logging and tracing in your resolvers to see how often fields are being accessed and who is using them. At some point you may decide to break backwards compatibility by deleting old fields, but you'll know exactly who you are breaking.
[+] [-] uyuioi|6 years ago|reply
Shopify says the main product is the store owner. But the developers pick up all of the slack of Shopify.
Recurring payments. App. Store backups. App. Theme backups. App. Order editing. Came late 2019. Checkout. So locked down. Where’s the API?! Slate tooling. Abandoned. Starter themes. Abandoned. Storefront SDK. Terrible documentation. More than 1 variant image? App. Metafields. App. Wholesale. App. Mailchimp. Removed.
People talk about google abandoning products. Shopify abandons nearly all developer tooling and is so locked down that it’s a constant “app for that” for the basics.
The interesting thing is theres been more than a few store owners I know that use Shopify. They’ve asked how to move off of Shopify.
I guess fulfilment is more important though. Right?!
[+] [-] theturtletalks|6 years ago|reply
I'm actually working on an open-source fulfillment and operations app for Shopify:
https://github.com/openshiporg/openship
I use it to build small apps that interact with the API directly instead of paying and relying on any apps.
[+] [-] MentallyRetired|6 years ago|reply
[+] [-] xal|6 years ago|reply
[+] [-] Waterluvian|6 years ago|reply
[+] [-] thsowers|6 years ago|reply
What was the change?
[+] [-] mmmannn|6 years ago|reply
[+] [-] joegahona|6 years ago|reply
What was the business?
[+] [-] keithwhor|6 years ago|reply
When you all think about API versioning, what makes the most sense to you — a semver approach (major.minor.patch) a la NPM, or a date-based approach (2020-01-07) a la AWS? Or is some combination of the two desirable?
At Standard Library [0] we both allow people to publish APIs but also publish API proxies on behalf of partners (Stripe, Slack + others) using a semver approach. It’s not perfect but theoretically enforceable (schema parameter additions can be forced to require a minor update, schema parameter removals can be forced to require a major update). We’ve just stuck to this semver approach based on intuition and haven’t had negative feedback about it, but I do like the idea of time-based versioning.
Would love thoughts! If you want to play around you can build your own APIs using https://code.stdlib.com/, which uses the FunctionScript specification [1] to enforce HTTP request schemas.
[0] https://stdlib.com/
[1] https://github.com/FunctionScript/FunctionScript
[+] [-] Waterluvian|6 years ago|reply
- Treat APIs as immutable.
- Any mutation results in a wholly new API version, not a patch or minor update.
- The developer will learn what changed, and how much changed by reading the patchnotes, not looking at which semver numbers changed. This is probably a healthy practice to encourage.
- Don't change APIs so much. The interface should be very carefully designed and tested and shipped like an NES cartridge: consider it impossible to fix once its shipped.
Therefore just start with `1` and increment each time.
The reason I don't like semver is that it's a developer convenience that leads to sloppy practices. You built your product against a specific API. If the API changes, you need to re-run your entire API evaluation, testing, blessing workflow. If the delta is tiny (what would have been a patch change) then yay, your task is likely going to be very simple. But you shouldn't see a bump version update and decide you can cut corners.
[+] [-] m12k|6 years ago|reply
[+] [-] Navarr|6 years ago|reply
Semver is important for knowing whether or not to evaluate for breakages. You can theoretically combine both of these by making the date the patch version or supplying it as a version metadata
[+] [-] solatic|6 years ago|reply
If you publish API version 1.0.0, and then internally you fix something and publish 1.0.1 (remember: a patch doesn't change the interface at all), should you continue to serve both the 1.0.0 and 1.0.1 APIs? How are they different from the consumer's perspective? What if your reason to release 1.0.1 is to fix a security issue - in what world is it ethical to continue to serve 1.0.0? If you can discontinue serving 1.0.0 at any time (because of a security patch released with 1.0.1), then you can't offer any long-term durability guarantees for early patch versions, and so indeed, offering older patch versions (when newer patches didn't fix security issues) is more likely to break your consumers when you're forced to discontinue older patch versions for security reasons, than if you refused to serve patch-level variants in the first place.
Because you, as upstream, have no control over whether the pure addition of fields will break downstream (since downstream may or may not be strict about what they accept), you should only offer semantically versioned minor releases if you're willing to guarantee durability for earlier minor versions, and can maintain multiple minor versions of your API in parallel. If not, then be explicit about the potential of your changes to break downstream - use timestamps and document sunset dates.
[+] [-] bmn__|6 years ago|reply
We're talking about HTTP APIs, right? Then neither. These are solutions with bad trade-offs for the problem at hand.
Version the link relations. This follows the principles that make the Web successful (a.k.a. REST). If that seems weird to you¹, consider the following:
You have a personal homepage type Web site. When you change or add or remove a document, do your users need to upgrade their user agent to keep using the site? Why not?
----
¹ Numerous developers are so enamoured with putting a version number into document URIs that they cannot fathom not doing it. This is another mutilation of the mind à la Dijkstra.
[+] [-] eapache|6 years ago|reply
[+] [-] thdrdt|6 years ago|reply
But with semver you can say: ok, I want every new version up to a new minor version (all fixes). While with a date based version you don't know how 'breaking' the changes will be.
[+] [-] posedge|6 years ago|reply
[+] [-] seelmobile|6 years ago|reply
Google's proposed a "stability" semantic as a third option[0]. TL;DR no breaking changes in the Stable channel but you can add backwards-compatible[1] features in-place.
A permanent Beta channel that's a superset of Stable lets users choose how change-tolerant they are. This lets API producers launch features earlier, knowing they will only impact risk tolerant users if breaking changes are needed. Theoretically this reduces the need for breaking changes in Stable, which require a new Major version.
[0] https://aip.dev/181 [1] https://aip.dev/180
[+] [-] NicoJuicy|6 years ago|reply
V2
V3-dev/v3-test/v3-rc - all related to how many changes you expect
V3
[+] [-] petetnt|6 years ago|reply
Sure, it's Shopify's choice. But considering how long their own changes take (for example multi-language is still in some sort of beta and it's been up and coming for like 5 years?) the API cycles are just brutal. And the saddest thing is that Shopify is still the best managed e-commerce platform for most usecases.
[0]: https://help.shopify.com/en/api/reference/inventory
[+] [-] james_s_tayler|6 years ago|reply
[+] [-] sour-taste|6 years ago|reply
[+] [-] auv5|6 years ago|reply
[+] [-] etxm|6 years ago|reply
[+] [-] pbreit|6 years ago|reply
[+] [-] leome|6 years ago|reply
[+] [-] lol666|6 years ago|reply
[+] [-] lol666|6 years ago|reply
[+] [-] theturtletalks|6 years ago|reply