Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

interesting. No more /api/v1 or /api/v2 in the urls. Now it's just /api/thing but pass in X-Api-Version in header and the version is a date not a number or 1.2.3 three numbers.


Author here. Unsurprisingly, we did have a lot of internet debate about how the version should be specified (header or path or query param) and we landed on using headers. But, to be completely honest, we didn’t find super strong arguments for any option, so it was just something where we had to be opinionated.


No one had a strong opinion on simplicity of “/foo/v2” vs “/foo” with a header variable of “2022-11-28.”

That seems really surprising.


A strong argument is different from a strong opinion. Sounds like there are lots of people who feel strongly but none of them can put up a strong argument showing why one is better than the other.


Simplicity is a pretty strong argument in my mind. This whole thread has been full of arguments against. Not sure how we settle on if they are strong enough.

It seems not strong enough for Microsoft. But that’s why I was surprised as being a not very useful hassle would seem to be a big reason not to do it that way.

Cynically, I fear this method allows lots more versions and lots more breaking changes so get ready for stuff that could have been backwards compatible now just be a breaking change with a new version.


> Simplicity is a pretty strong argument in my mind.

But the URL version inly wins that argument if you ignore all the issues.

Github clearly wants to iterate their REST API faster and feels constrained, the URL method would mean either the entire API versions over quickly with almost but not quite copy pastes, or endpoints get versioned individually and you’re simultaneously hitting /3/foo and /42/bar with little rhyme or reason.

Things get confusing when you retrieve an entity via /6/ then update it via /4/.


For end users the api should be effectively versionless (i.e backwards compatible in all possible cases).

Most api consumers don’t care if fields are added or new methods added they are not using, thus they don’t care much about the version unless upgrading. Removing things or changing behaviour should be very rare and have a very good justification.


I just posted an argument for not-headers here: https://news.ycombinator.com/item?id=33782541

Short version: I do initial research against APIs using web browsers a LOT, including on my phone, and headers are really inconvenient for that.

In case you can't see DEAD comments, there's one here that talks about this too (and isn't offensive about it, so I imagine the account is DEAD for some other reason): https://news.ycombinator.com/item?id=33781215


I just vouched for it: you’re right, it seems a very reasonable comment.


I think the version should only be in the URL if you intend to support that URL forever.

I am of the opinion URLs are a promise the document will be available until the end of time. I don’t want to break the internet with 404s.

Since the only promise here is for the API to exist for 2 years when depreciated, then I think using a header is appropriate.

Query param doesn’t make sense either as it is often used as input data into the document at the URL. The document schema depends on the API version, so query params become a chicken/egg scenario


Surely, impact to API consumers was considered in changing to a header based approach. What if a company is using an API proxy like Apigee and now has to go through a change control process to incorporate a new custom header on outbound requests? If the 2 options for passing versioning ended up as a toss up, what arguments for a header based design outweighed the negative impacts to consumers?


One thing is not obvious to me: would the header accept any date and use that as a tie-breaker, or would the header only accept “version” dates?

Let’s say I start coding against the API today, can I just set the header to “today” and github will dereference that to the nearest preceding version, or do I have to look up and set the header for each endpoint?

IME it’s common to have a “broker” which handles all calls to the API in order to do common ore and post processing, having to look up and specify versions for each call sites would be frustrating, as well as the risk of different calls using different versions.


I went through this debate recently also and also ended up realizing there was no clear cut winner, but we did end up coming down on the same method of using headers. But oddly I get anxiety when I see other APIs that have gone for a different choice! Also considered that maybe could do all 3, or header || query parameter (which we may still do).


Why pick date version over semver? (Date versioning fan here)


A silly reason I can think of is Date would last longer than semver. I see some moved on already (e.g. https://gitlab.com/staltz/comver)


I also found it odd that they switched from URL versioning to an HTTP header.

Sure if I’m writing an integration that’s no big deal, but it makes it more of a PITA to do things from the CLI with curl.


Well, you don’t need the header


You don’t need it if you’re expecting the current 2022-11-28 version, but it seems like you’ll need it to “opt-in” to any future ones, which presumably includes new endpoints that don’t even exist in the current.


Author here. We consider new endpoints to be non-breaking changes, so they’ll be available in 2022-11-28.


Ah, ok. Well personally that seems even worse. Adding new things to the existing “version” just seems crazy.


depends on how you define "version" here. seems that github took "version changes when backwards compatibility is broken" version which can mean that you can deploy 20 new features but if those are new then "version" does not change.

that would be my guess


Looks pretty similar to Stripe's API versioning method: https://stripe.com/docs/upgrades#api-versions


A great blogpost on the architecture Stripe chose for calendar based versioning: https://stripe.com/blog/api-versioning

In essence, they transform requests from the latest API version back to the (older) versions by writing an adapter for each response for every breaking change. Not sure what they do for new endpoints, or when a future response no longer includes a field that used to be included before.

Some nice things though:

- timeline they suggest seems to be longer than GitHub (implied by the “a power company should not change its voltage every 2 years” statement)

- instead of a header, it’s linked to the account of the user and the time of the first API call. That might be a good default for GH rather than the oldest available version at that time as suggested elsewhere in the comments. (drives adoption, least surprise, longest expected validity of the interface)

Edit: formatting


Stripe also allows a header to declare the version, which is how you should upgrade (test each endpoint one by one, and when they're all migrated, upgrade the account api version).

I personally find this a much saner way to declare api versions (version in header). Specially if the platform supports Webhooks which have urls in the payload: if your API declares version in the path, which version do you use in the webhooks urls?


The major problem it seems to me with these versioning approaches is that at some point you want a non-breaking change that arrived after a breaking change. Then what?


Finally someone does versioning properly and enforce basic REST principles. URL versioning (v1,v2, etc) simply don't make any sense yet they are used all the time. API shouldn't ever change (on that version) once you release the version, yet we see new endpoints appear after v1/ all the time. You never know what's new what old feature in url versioning.

Imagine you release package on npm (or whatever) versioning it with v1 and constantly adding new features and still keeping the v1 version tag. That's what you're doing to your API.


The difference is purely cosmetic: version could have been stuffed into URL as /api/2022-11-28 and nothing would change.


It's much more ergonomic and compact to use as an end user than a dedicated header, IMO.


Agreed, pragmatically a separate header is a dubious choice.

Semantically it is equivalent though.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: