inadarei / rfc-healthcheck

Health Check Response RFC Draft for HTTP APIs

Home Page:https://inadarei.github.io/rfc-healthcheck/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Version number considerations

christianhujer opened this issue · comments

General practice disagrees with a statement made about version numbers in the (otherwise great, I already recommend this to projects!) draft. The draft states:

in well-designed APIs, backwards-compatible changes in the service should not update a version number.

However, a lot of people follow semantic versioning, which has the benefit that it is possible to tell, by comparing two version numbers, whether the difference is bugfixes (1.2.7 to 1.2.8), new features (1.2.7 to 1.3.0), or compatibility-breaking changes (1.2.7 to 2.0.0).

I would like to interpret version to be a version that ideally would follow semantic versioning, and releaseID can be something quite customized, like a git commit hash or build number.

Therefore I suggest to change the statement "in well-designed APIs, backwards-compatible changes in the service should not update a version number" to "in well-design APIs, it can be told from update of a version number whether the changes are backwards-compatible".

In case readers haven't heard of semantic versioning, here's a link: https://semver.org/

From the sample JSON, version appears to be a single digit. Since the RFC lacks an implementation, version could be just two numbers, “1” for this RFC, then “1.1” for the first backwards compatible RFC, etc.

SemVer is kind of overkill of the RFC (and, FYI, OSGi’s 2010 Semantic Versioning whitepaper is better than SemVer anyway).

But I think maybe the RFC is saying it will define “1”, and if you extend “1” you don’t need to change the version number.

What I’m curious about, are we waiting for “2” from a future RFC? Or are implementators and users expected to increment version without a future RFC?

I'm not saying the RFC shall say people have to use SemVer. But people should be free to use SemVer and find that compliant with SemVer.

I did not know about OSGi's Semantic Versioning. I've tried to find the whitepaper, and what I found was this: https://www.osgi.org/wp-content/uploads/SemanticVersioning.pdf Having skimmed through it, my first impression is that it's actually not better than SemVer, I think its examples are too implementation-specific. Still, I'll read it in detail. Anyway, with SemVer vs OSGi's SemVer we're discussing something that probably shouldn't be referenced directly by the RFC anyway, the RFC should just make it possible and even suggest that people use something like these.

Semantic versioning was designed for and is commonly used in libraries and applications. It is not widely used for web APIs and is actually considered an anti-pattern because it is bad user-experience to break an API client for non-breaking changes in APIs. APIs and libraries are not the same thing. Several major APIs, widely praised for their API design excellence such as Stripe and Twilio actually use dates as their versions, so clearly they do not use semantic versioning,

Case in point, Stripe API (https://stripe.com/docs/api/versioning):

When we make backwards-incompatible changes to the API, we release new, dated versions. The current version is 2018-09-24. Read our API upgrades guide to see our API changelog and to learn more about backwards compatibility.

On the flip side of the coin, Google API versioning does claim that they use semantic versioning, but actually they don't because in the URIs they only use major version:

Note that the minor version is not encoded in the URL. That means that if we enhance the Cool Cloud API by adding a new field, you may one day be surprised when a call to coolcloudapi.googleapis.com/v1/coolthings/12301221312132 starts returning some additional data. But we’ll never "break" your app by removing fields.

the semantic version is returned in the headers, so it is actually more of what the health check RFC defines as releaseID, except instead of having two different things and calling both of them "version", we tried to make things less confusing.

I actually had a conversation with SemVer.org’s author and got him to admit SemVer is identical to OSGi’s Semantic Versioning.

While I think Semantic Versions can and does apply to web services, I want the client to negotiate the major (and possibly minor) version via hypermedia content type parameters. (Minor version negotiation might be desirable when added attributes can crash clients, e.g., JAXB JSON implementations like Jackson.)

Versioning of APIs is one of those things that people do in wildly different ways and there is no agreement on (as witnessed by some of the examples I posted above). Given how this subject surfaced here, triggering debate, as well, here's an alternative thought (with two options):

  1. Mild option: remove any assumptions about how versions are used, hence remove releaseId property from this spec and just leave the loosely-defined "version" property.

  2. Radical option: remove both "version" and "releaseId" properties from health check response, since it could be argued that they have no place in health check response and other things (e.g. "health" link relation from the API itself or a mapping in kubernetes deployment etc.) will make it evident which API and version the response in question pertains to.

cc: @dret @randallsquared

Now I see a much bigger issue with version, from this discussion.

Some people interpret version to be the version of the /health API itself, and thus the RFC.

Some people interpret version to be the version of the API for which /health is reporting the health status. That's how I interpreted /version.

The RFC should be clear about what it means with version. Currently, the RFC says:

  • version: (optional) public version of the service.
  • releaseID: (optional) in well-designed APIs, backwards-compatible changes in the service should not update a version number. APIs usually change their version number as infrequently as possible, to preserve stable interface. However implementation of an API may change much more frequently, which leads to the importance of having separate “release number” or “releaseID” that is different from the public version of the API.

I interpreted this as being the version and releaseID of the service for which the health is reported. But now I'm not sure whether I'm interpreting the RFC draft correctly.

@inadarei When applying semantic versioning to path-versioned APIs, using only the major version is not a deviation from semantic versioning, it is implicitly and effectivly a mandatory requirement by semantic versioning. The path is part of the API, and changing the path is a breaking change, thus the path has to be stable as long as there are no backwards-incompatible changes. So, the path must be the same between 1.0.0 and 1.0.1 or 1.1.0, and thus only the major version must appear in the path. Google is doing everything right there. It could be argued that it could be done with changes in the path, but that in turn would be break HTTP caching and make clients unnecessarily complicated. Google is actually following semantic versioning by the letter.

Because version means different things to different people, as evidenced by this issue's discussion, I would support just omitting it in favor of release or the existing releaseId, which is more clearly implementation dependent. If both were present, the releaseId would likely include all the information that version conveys anyway, as in the example, so removing version doesn't impact searching or filtering on healthcheck responses.

@randallsquared why would a health check endpoint report any version or releaseId of the main API? What is the benefit?

@dret I think home link should be a MAY statement. I can see many people deciding that the pointer should be from the API to the healthcheck and not vice versa, while other may like the direction, so this spec should avoid over-prescribing either of the approaches?

To avoid confusion about version it may be removed and 2 optional fields introduced with clear names:

  1. apiVersion
  2. monitoredServiceVersion