12 ms·
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.
by keithwhor 7y ago
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/ https://code.stdlib.com/, which uses the FunctionScript specification [1] to enforce HTTP request schemas.
[0] https://stdlib.com/ https://stdlib.com/
[1] https://github.com/FunctionScript/FunctionScript https://github.com/FunctionScript/FunctionScript
- Navarr 7y agoI 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
- Znafon 7y ago> I do not know any advantage of date-based versioning It can be very useful for continuous improvement and handling forward compatibility. Here's how Stripe do it:https://stripe.com/en-fr/blog/api-versioning https://stripe.com/en-fr/blog/api-versioning, we found that very convenient.
- keithwhor 7y agoI think it’s the implied stability, especially for long-standing APIs. S3’s API is 2006-03-01 — meaning there haven’t been breaking changes in over 13 years. This creates a psychological contract with developers that nothing’s changing anytime soon. The trade off is that AWS has some godawful APIs (DynamoDB has the least intuitive API I have ever worked with). But they’re stable. If you go with a date-based approach and create a process + contract whereby you guarantee API stability and / or deprecation date, the developer always knows exactly how much time they have before an upgrade.
- thdrdt 7y agoBoth don't say much without good changelog. 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.
- keithwhor 7y agoHow often do you feel like you find / consume changelogs when you’re looking at APIs? I feel like they’re either non-existent or non-obvious for most SaaS companies. Do you feel like you’d trust a company’s API more if you could peruse the API changelog more easily?
- thdrdt 7y agoIt's not about trusting a company. It's about wanting to know what will break if you start using a newer version of an API. And sometimes you also discover new features that can be really useful.
- keithwhor 7y agoThat’s fair! From my perspective that’s something that’s building trust with the SaaS provider. We’ve all heard, “ugh! This API is so shitty!” before — I’m fascinated by what tools and products can be built to prevent developers from feeling these pains. Thanks for the feedback. Can definitely see that changelogs are really underinvested in across the industry, and it’s useful to have people mention how valuable they are.
- k__ 7y agoI never understood why we settled on semver. If I have a bug, it's a breaking change. If I fix this bug, it's also a breaking change.
- tclancy 7y agoYour presumption of a collective "we" is at the root of the problem: versioning is one of those things in development where the same word can mean very different things to different people (see also: agile, automated testing). Prior to semantic versioning, there was an approach that looked like an agreed-upon standard to consumers of software packages/ libraries/ APIs but was not. On one extreme you had certain developers (who I am sympathetic to because it's my personality) who were loath to ever label something 1.0 because it implies Doneness and a freedom from bugs that can never be so. On the other side you had people at chop shops who would bump the major version of some boxed product every time they fixed two bugs. Even if you as a good developer did the research to discover that 0.7.6 of Package A was solid and 5 years older than Package B 7.6, there was a good chance someone above you would declare 7.6 > 0.7.6 and that it was paid-for software so "We can get support from them" and force you to work with a shittier product. So "we" developers do what we almost always do: we cast about for a better solution and landed on one that made the meaning of version numbers opaque to anyone outside the guild of mages writing code. In short, semantic versioning may suck hard, but it sucks less.
- eapache 7y agoMy 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.
- keithwhor 7y agoWhat specifically about SaaS API constraints do you think makes a date-based approach more appealing than semver? My high-level feeling is that it’s just way more difficult to ship a SaaS API than a Ruby Gem, so adding semver to that is just another layer of API management everybody has to agree on. Do you agree with this assessment?
- eapache 7y agoFor most published libraries every old version is always available and upgrading is never mandatory. For SaaS APIs the constraints of the business means that very few businesses (Stripe being the notable exception) want to support more than a handful of versions at a time, which results in versions regularly reaching end-of-life and completely disappearing. In this world, upgrading to newer APIs is mandatory and fairly frequent. The result is that API version handles need to prioritize different information. Libraries need a format that makes it easy to ballpark the size of changes between arbitrary not-strictly-sequential versions. SaaS APIs need a form that makes it easy to infer support windows and end-of-life status.
- keithwhor 7y agoGood points. Do you think a combination of the two is possible, and how do you think that might work? year.month.minor.patch?
- ahnick 7y ago> "SaaS APIs need a form that makes it easy to infer support windows and end-of-life status." You can infer that from the Release Date though. (e.g. Version 3.4.2 Released on 2019-12-17) To me the power of Semver is that it conveys complex relations between version iterations. For example: - 3.4.1 -> 3.4.2 is just fixing bugs in existing functionality - 3.4.2 -> 3.5.0 is an upgrade containing non-breaking changes - 3.5.0 -> 4.0.0 is an upgrade containing breaking changes As a developer, Semver + release date seems to convey everything date based versioning does plus I get the advantage of understanding at glance the importance and risk profile of each release. Note, this system does not reduce my obligation to run my own tests to verify that the version has in fact lived up to its intention (i.e. a minor version bump did not introduce a breaking change). Even though my obligation is not reduced, it does act as a filter to help prioritize development time for evaluation of performing upgrades.
- m12k 7y agoI 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).
- keithwhor 7y agoWould a tool that easily shows you documentation / schema changes between API versions be useful to you, or create more trust with a SaaS provider if they offered it?
- gen220 7y agoI’m pretty sure the semver definition for a x.2.x change is that you’re adding surface area to your interface, in a way that doesn’t overlap with existing surface area, i.e. bytes sent over the wire for x.1.x will result in the same response bytes for x.2.x, all else being equal. Note that the phrase non-overlapping is where all the complexity is hidden; it’s actually tricky to guarantee that an addition hasn’t changed any existing queries. For example, adding an enum value will mess up clients who query with max(enum_value). Technically, they’re not sending the same bytes, so the change is non-overlapping, but the client might disagree :)
- Waterluvian 7y agoI 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.
- keithwhor 7y agoI don’t disagree with your assessment on semver as it currently exists for, say, NPM packages. I do think that with web APIs specifically, the surface area is a lot smaller — the HTTP interface is literally all you touch — so semver, in its purest form, is actually completely enforceable as long as you understand the API schema. Our team has talked a lot about either hard-enforcing or automatically applying semver where applicable. My question to you is — does this sound reasonable, and if you knew a semver contract was actually bound to implementation (i.e. guaranteed and not implied), would you trust it more?
- setr 7y agoTbh, it doesn't matter. But the semantics should be such that it doesn't matter -- the user of the API shouldn't care whether it's implementation bounded or not. But Everytime you break that abstraction, trust in the abstraction is necessarily reduced. Bounding to implementation is just the easiest way out -- if your policies, tests and protocols consistently fail to uphold that abstraction, then the you can fallback to this very simple (presumably innefficient) strategy to do so. But I, as a dev, just want a stable API, and I don't care how it's done.
- seelmobile 7y ago(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. [0] https://aip.dev/181 https://aip.dev/181 [1] https://aip.dev/180 https://aip.dev/180
- keithwhor 7y agoDo you think it’s possible to achieve the same sort of system by just using semver + release candidates? For example, 5.x.x is currently stable, so you release 6.0.0-rc1 (2, 3, ...)?
- posedge 7y agoHonestly, 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.
- bmn__ 7y ago> 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.
- NicoJuicy 7y agoV1 V2 V3-dev/v3-test/v3-rc - all related to how many changes you expect V3
- solatic 7y agoWhat's the point of a patch release for an API? 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.