mpetrovich a year ago

Making the version optional is a bad idea, IMO. It lets consumers use your API without versioning, which is like telling them, “here, create a time bomb that may or may not explode when the next breaking api change is released.”

There’s zero practical user benefit to NOT specifying the version, so why not enforce it? You’re creating a footgun otherwise.

Furthermore as others have pointed out, this is a purely cosmetic change. Using /api/$UNIQUE_STR in the URL vs. X-GitHub-Version: $UNIQUE_STR in the header are functionally equivalent, so…

Why bother making this change then? What was suboptimal about the previous way? What benefit does this bring?

EDIT: Serves me right for not reading the docs more carefully. From the docs [1]:

Requests without the X-GitHub-Api-Version header will default to use the 2022-11-28 version.

See phphphphp’s reply below for context.

[1] https://docs.github.com/en/rest/overview/api-versions?apiVer...

  • phphphphp a year ago

    The version is only optional in the request: when it isn’t provided in the request then the system will default to the version of the API that would have been given had this new system not been implemented, so there is no change in behaviour for existing implementations, there are no footguns or impending explosions. The key point is when they refer to today, they don’t mean “today as of the time of the request” they mean today, as a point in time: November 28th, 2022.

    The benefit of this new versioning approach is that they can maintain backwards compatibility for existing implementations while introducing breaking changes for new implementations to benefit from.

    In an ideal world, sure, they should have implemented this from the start, but they didn’t, so going forward they have to accommodate both existing implementations (by not breaking anything) and new implementations (by providing features that would break existing implementations).

    They’re basically just implementing stripe’s well-understood and battle tested approach for versioning, while maintaining backwards compatibility. I am struggling to see any problems with the approach, in fact, it seems like the only right approach (?)

    • mpetrovich a year ago

      Oh, so you’re saying that omitting the version from the header will ALWAYS return this specific version, NOT the latest version?

      That certainly addresses the footgun I mentioned.

      EDIT: From the docs: Requests without the X-GitHub-Api-Version header will default to use the 2022-11-28 version.

      • phphphphp a year ago

        Yes, exactly, that’s my understanding from the blog post and changelog post. I think perhaps the waters have been muddied a little because of our expectations of API versioning + GitHub’s need to maintain backwards compatibility for existing implementations. The way I understand is that “v3” of the API (which has been around for 10 years) is now version “2022-11-28” and so existing implementations while be using version “2022-11-28” without any required change.

        • buro9 a year ago

          I don't see how that can be true when new versions put a 2y time bomb on old versions.

          At some point the default 28th Nov 2022 version is unsupported... So what is served must change... Either it's the latest at the time or the behaviour of receiving a response without providing the header is removed.

        • saghm a year ago

          I often find it useful to think of things like this as "no version specified" being the lowest version, and then explicit versions start counting after that.

    • timrogers a year ago

      Author here. You've put it better than I could have put it myself. Thanks!

  • prepend a year ago

    Because they use calendar versioning the user benefit to not specifying is not having to keep track of a million different dates.

    I work with a lot of APIs that just have “/v1” or “/v2” as part of the endpoint so it’s easy to specify. Setting a header parameter to a specific date is a PITA and I certainly don’t want to look up the curl syntax whenever I do simple work.

    I think if they made it required, it would annoy people. So they didn’t.

    • mpetrovich a year ago

      I agree with you, so then why not stick with the previous /api/v2 url approach?

      And it would annoy me far more if my app that talks to GitHub inexplicably breaks in a few years because of a release I wasn’t aware of.

      These changes make no sense to me.

      • knome a year ago

        It enforces the idea that the API is versioned per endpoint and not overall.

        You can update each endpoint in non-backwards compatible ways without having to update all of them.

        If you have it in the URL, it's likely libraries and custom scripts will just set the value in configuration instead of per-callsite.

        You don't want to have to wait to fix up some API or make non-updates on all the rest just to release a new version.

        edit: I agree that making it optional is a bad move

        • mpetrovich a year ago

          You’re saying that it’s LESS likely to specify the API path at the callsite than the HTTP header? o_O

          • knome a year ago

            I was saying that having /v2/ in the URL implies all of the endpoints will have a /v2/ and that they will be part of a matched set. Having it in the header makes for having version numbers that are targeted at each specific endpoint without making it look like the others are affected.

            Here specifically I was suggesting that the /v2/ in the URL would make people using the API more likely to set the /v2/ in their app to use it across all calls rather than different per endpoint versions.

            It looks like the calls are in the version rather than the version being in the calls.

            • robertlagrant a year ago

              I don't see the difference. For this call I'm using the v2 API. For this one the v3. It's much harder to wrangle headers than to do this, IMO.

              • WorldMaker a year ago

                I see it a bit as a REST thing: /v2/some/endpoint implies that v2 "owns" "some/endpoint". What state does "v2" represent? Does it own every "some", just "some/endpoint"? What is that hierarchical relationship really?

                It's a general problem I see with any REST API that tries to version that way. Version isn't a strict hierarchical "owns" relationship and embedding it early in the URL sort of violates REST principles in what the folder path of a URL is meant to imply. (Admittedly, that's a bit of a more strict interpretation than most people pragmatically follow when building REST APIs, but it is an interesting and useful strict interpretation so worth bringing up and examining.)

                If you are versioning individual endpoints it might make sense to use URLs like: some/endpoint/v1 and some/endpoint/v2

                That makes it more clear that "some/endpoint" "owns" that relationship and kind of/sort of what "exactly" is being versioned. If you must put a version number in the URL.

                That said, REST has always been about content negotiation as a long held tenet, and that has always been about using the right Headers (since the earliest HTTP 1.x apps), and I've always felt like complaints that you can't send the right version Header reflects primarily on bad tools that don't understand HTTP (and REST) as well as they should. Sending Headers is an important part of HTTP. I don't think it should be harder to send the right Headers than to embed things that would be Headers into the URL. Though that's just my somewhat strong opinion on the subject.

                • robertlagrant a year ago

                  I get that instinct, and used to agree with it.

                  But now I think that the versions are not part of the resource path, just as starting the path with /api doesn't mean the API is part of the resource. The versions are part of the api path. If I had a URL that was /api-v1 and /api-v2 it would be more obvious, perhaps, that the URLs are addressing different API implementations, and everything that came after was the RESTful bit. Having /api/v1 and api/v2 as the prefixes isn't quite as clear on that point, for sure.

                  The reason I like having the version in the path is it makes it a bigger deal for the implementer when they're making breaking changes. Having a per-endpoint, slightly hidden way of versioning endpoints might mean it's harder for them to intuit quite how many changes are flying at their API's consumers.

                  • WorldMaker a year ago

                    I've also argued against /api for similar reasons under "strict REST", too. It's often "redundant" information not all that useful in the URL hierarchy. Everything is an API. There's no "non-API" endpoints. Even pages that only return HTML are a public API. But I also understand the usefulness for things like security audits to have a magic circle labeled /api even though that magic circle doesn't really do anything.

                    I think you are almost assuming the implementer doesn't abstract the URL routing in some way and has to care about about every possible URL route? In practice there is often very little difference on the implementer side between routing `/api/v{id}/some/route` and pulling in the version number as a route parameter and picking up a version number from a header. The implementer is still just seeing a version number parameter injected either from their routing framework or their header dictionary. In both cases the amount of code they see is roughly the same. In many cases all "versions" of the code might only be a single "Controller". Outside of a few languages such as PHP there's few languages that have a direct 1:1 between URL paths and "Controllers" or "Source Files" or anything resembling that. At some point there's always some intuition that side-by-side versions imply side-by-side code and the "tech debt" sense of that maintenance version, but that intuition comes from numbers of classes and size of classes and indirect measurements like PR cycle time and Sprint planning estimates, and is never directly connected to URL routing versus Header negotiation. (Again, in modern languages with modern routing frameworks. Obviously if you have to drop a PHP or ColdFusion file for every API endpoint you'll feel URLs a lot more painfully than that.)

          • johtso a year ago

            In reality it's probably completely equivalent. The version prefix in the path would probably live in some GITHUB_API_URL variable. The header would be set on the http session object.

    • skeeter2020 a year ago

      >> Setting a header parameter to a specific date is a PITA

      Really? Don't you do this all the time for everything beyond a simple GET? From my experience it's far preferable than URL versioning because you can version on an individual endpoint. Right now I'm consuming Versions 0.9, 1.0 and 2.0 from a vendor; it would be way nicer to set individual headers than somehow mananging multiple endpoints.

      • prepend a year ago

        I’m not saying that it’s difficult, just that it’s a pain in the rear end. You can’t cut and paste calls, you have to have two variables to see the version. It’s just minor annoyances.

        That being said, I don’t call multiple versions of an api so maybe it’s nice to just be able to call multiples.

        I imagine that it’s pretty easy to manage with the url version as you’re still just storing one endpoint and appending the version depending on what you would like. So programmatically you just change a config variable with the version you want.

        But it seems like the version on changes with breaks so if you’re calling different versions you have to handle the input and output differently anyway.

      • robertlagrant a year ago

        > it would be way nicer to set individual headers than somehow mananging multiple endpoints

        I don't understand the difference. What makes it nicer?

    • RileyJames a year ago

      Is there an easy way to set a ‘sticky’ curl header, or change/set the default headers?

      I did a quick search and couldn’t see any docs on this use case.

      • mdaniel a year ago

        Behold the power of curlrc: https://curl.se/docs/manpage.html#-K

        And it behaves the same way as `curl --header "" --header ""` in that it's additive:

            $ curl -vK <(printf 'header = "alpha: 1"\nheader = "beta: 2"\n') httpbin.org/get
  • pbreit a year ago

    Why version at all? They went 10 years without versioning. The reasons stated for needing versioning are weak at best ("like deleting a response field, making an optional parameter required, or deleting an endpoint entirely"). Just retain backwards compatibility. Once you have versioning, people feel more comfortable making incompatible updates which is a terrible direction.

    • sitkack a year ago

      > Just retain backwards compatibility.

      I'll take the bait, like C++?

      > comfortable making incompatible updates

      Incompatible how? You specify the version, your semantics are of that version.

      • masklinn a year ago

        Until github removes that version, which they will:

        > When a new REST API version is released, we’re committed to supporting the previous version for at least two years (24 months).

    • WorldMaker a year ago

      I think that's an interesting question here. Especially with how much was invested into GitHub's GraphQL and the once implication that the GraphQL API would entirely replace the REST API. (And the GraphQL API still having its own, different versioning scheme now and after the change mentioned in this blog post.)

      The blog post suggests that we might find out some of the API changes that warranted this change as early as next month, but it would have been great for this blog post to include some examples of changes that were coming.

  • thrwawy74 a year ago

    You could make the argument that having it in the URL is better than using a custom header, because customer headers may see mangling/blocking in corporate environments (indiscriminantly).

    I don't think it's a strong point.

micimize a year ago

Much more important than moving the version request to the header is that it should hopefully clear up the confusion caused by REST being "v3" and GraphQL being "v4."

As someone who has written some fairly intensive github integrations, I think the biggest thing github could do DX wise is unify the systems behind the two APIs and make them actually have parity. I'd always need both because they are a Venn Diagram, feature-wise.

Though to be fair, I'm guessing it is a very difficult problem that every engineer on both teams dwells on regularly.

  • onionisafruit a year ago

    Making a public graphql api was a huge misstep.They barely had an internal graphql api when they released the public api.

    • larusso a year ago

      I agree. I had the feeling when it was released years ago that the whole v4 API was kinda generated from a given database Schema. It was very usefull if you wanted to fetch data in a similar way than how the website does it. e.g. pull requests are part of repositories. I for instance wanted to query all pull requests for a given org and filter based on repositories. The search api was in most cases the only solution or go and mix and match with the v3 API.

    • niels_bom a year ago

      I remember something from the Changelog podcast that Github has a number of single- or two-person projects that are experiments but are then used by a non-insignificant amount of people. So then they choose to keep it on life support with minimal resource expenditure.

    • rogerz46 a year ago

      I agree. Graphql API is basically a database schema. That thing is lifting GH from heavy burden and will mislead their consumers like us.

simonw a year ago

I kind of wish this wasn't only in a header.

I frequently perform basic API exploration in my web browser - hitting https://api.github.com/repos/simonw/datasette/commits for example - and there isn't a convenient way to add headers to those requests.

I quite often do this kind of exploration on my iPhone in Mobile Safari (no need to get my laptop out just to quickly check what shape an API is), which is even less convenient for sending custom headers.

  • sitkack a year ago

    When I was younger, I wanted everything to be absolutely correct and small pragmatic perturbations to a design really irked me.

    For the last 12 years I have put the api version in the url, it is unambiguous and has never caused a problem. In-band is best, esp for a public service that is consumed in an ad-hoc manner (wget, curl, any http library you can find). Hell, even include the docs in the response.

    Hit https://old.reddit.com/domain/simonwillison.net/.json in Firefox and you can get a long way.

  • brandur a year ago

    I wonder if they might be amenable on that one. (If any GitHub staff is reading this.)

    The proposed scheme is fairly similar to Stripe's, and although Stripe mainly uses the `Stripe-Version` header for versioning, its API has also always allowed you to alternatively send a `?_stripe_version=<version>` query parameter which it'll respect.

    Stripe's API implementation definitely had its share of legacy cruft that added up and made things more difficult to maintain, but out of all of it, I don't remember having to support that alternative `_stripe_version` parameter ever really being a maintenance headache. The leading underscore made sure that even as the API expanded, it never accidentally collided with parameters on any other endpoint.

    • timrogers a year ago

      Author here. I think this is an interesting suggestion, and one we'll have a chat about internally.

      I definitely see the benefit of being able to easily hit the URL from a browser - although I think it's probably only relevant for unauthenticated GET requests.

      • nerdile a year ago

        It's also relevant to authenticated GET requests. See also: how VSSPS, ARM do it.

    • onionisafruit a year ago

      Old github would have been amenable. Current github has to convince a product manager who barely knows what an api is.

      • prepend a year ago

        This definitely seems like a “current GitHub” decision where some project manager chain signed off after being “trained” on the benefits. It has a very Microsoft feel of a technically correct if everything goes right (eg, windows registry) but overly complicated and not something that would survive an open source dev review.

        That being said, it’s not the end of the world. It’s just eventually these straws will add up to something really bad.

  • mdaniel a year ago

    Reasonable people can differ about the word "convenient," but both Chrome and Firefox offer a "copy as fetch" for any request in the Network tab, enabling playing around with requests and responses in the Console

    An alternative answer to this question is: well, if it's basic then you don't care about which version they respond with :-D

  • onionisafruit a year ago

    I agree with you that it’s useful to be able to explore apis from your browser’s address bar. But exploring an api from mobile safari is too hard core for me.

andrewfromx a year ago

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.

  • timrogers a year ago

    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.

    • prepend a year ago

      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.

      • Gigachad a year ago

        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.

        • prepend a year ago

          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.

          • masklinn a year ago

            > 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/.

      • grey-area a year ago

        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.

    • simonw a year ago

      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

      • macintux a year ago

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

    • pyrolistical a year ago

      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

    • prng2021 a year ago

      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?

    • masklinn a year ago

      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.

    • keithnz a year ago

      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).

  • chrismeller a year ago

    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.

    • datapolitical a year ago

      Well, you don’t need the header

      • chrismeller a year ago

        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.

        • timrogers a year ago

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

          • chrismeller a year ago

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

            • akoncius a year ago

              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

  • dividuum a year ago

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

    • hectormalot a year ago

      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

      • chucke a year ago

        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?

      • pbreit a year ago

        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?

  • mirzap a year ago

    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.

  • dottedmag a year ago

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

    • CharlieDigital a year ago

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

      • dottedmag a year ago

        Agreed, pragmatically a separate header is a dubious choice.

        Semantically it is equivalent though.

64bittechie a year ago

Could you elaborate why did you pick using a header instead of making the version number part of the URL?

If the version is part of the URL, you have benefits such as -

1. It's easy to inspect the URL. For headers it's more involved.

2. Middleware (Proxies) can drop headers that they don't recognize.

3. Logging frameworks typically log URLs but don't log headers OOTB.

As someone who has built a lot of APIs for a long time, I think the decision to use headers to communicate versioning information is not optimal.

okhudeira a year ago

All the discussions around header vs. path versioning reminds me of this post by Troy Hunt of Have I been Pwned.

> In the end, I decided the fairest, most balanced way was to piss everyone off equally. Of course I’m talking about API versioning and not since the great “tabs versus spaces” debate have I seen so many strong beliefs in entirely different camps.

https://www.troyhunt.com/your-api-versioning-is-wrong-which-...

garblegarble a year ago

It's not awfully promising that the article talks about "X-GitHub-Api-Version" and then calls it "X-GitHub-Version" later on...

Edit: it's been fixed now, good Github doc fix response time!

  • timrogers a year ago

    Author here! Thank you so much for flagging this - it’s amazing what can sneak through so many proof reading attempts. We’ll get this fixed.

    • Too a year ago

      This should be argument enough that /v2 is a lot less susceptible to such error.

      Please also return error if the wrong key was used, instead of silent fallback of assuming no version.

  • pkulak a year ago

    I thought the days of prefacing vendor headers with "X-" were long over.

    • sdiacom a year ago

      Especially if you're already prefixing it with "GitHub-". Like, you're definitely not going to step over the HTTP standard headers any time soon.

      That said, the overall concept is deprecated: https://www.rfc-editor.org/rfc/rfc6648

    • Gigachad a year ago

      The reason for that was eventually these headers became real standards and software would break when the X was removed. I very much doubt we will ever see github api headers added in to nginx and apache.

      So if your header is something you think should/will be standard but it's still in beta/testing, give it the real name.

    • timrogers a year ago

      Author here. We just wanted to be consistent here with other GitHub-specific headers prefixed this way. We could have had different headers with different formats or tried to change the formats of the old headers, but we figured this was the sanest way.

      • leipert a year ago

        How would you do that breaking change then? (slightly in jest ;))

mdaniel a year ago

A friendly reminder that if you're going to use in these "date-ish" headers in a yaml context, ensure that you quote the value because `x-github-api-version: 2022-11-28` deserializes as `{"x-github-api-version":"2022-11-28T00:00:00"}`

  • macintux a year ago

    Argh. Man, that’s a pet peeve of mine in general: it feels like most languages really want a date value to be a datetime. I’ve had to write libraries for Erlang and Python to support concepts like “August” without interpreting them as “August 1st 00:00:00”.

dgudkov a year ago

>When we do decide to end support for an old version, we’ll announce that on our blog and via email.

Blogs/email shouldn't be the only channel. Announce the retirement date with an HTTP header right in the response. In this way, developers can create automated alerts about soon depreciation.

In other words, depreciation announcements should be machine-friendly, not just user-friendly.

  • timrogers a year ago

    Author here. I think it's a pretty nice idea to include deprecation information in the response headers as another way to keep API integrators informed - alongside written forms of communication like blog posts, changelog entries and emails. We'll discuss that internally.

    As @xavdid noted, the reality is that most people won't see these headers, but it's a nice power user feature.

  • xavdid a year ago

    I like the idea of this, but in practice, I'm not looking at responses for an API unless I'm actively developing on it. If I'm doing a maintain-occasionally project, then I'll never see the deprecation response. I'm also unlikely to set up an automated alert for a random API I used one time (tricky to do without external tooling, like Zapier). I'm much more likely to plug an email into a form, since that's all set up for me already.

    That said, as long as there's multiple ways to get notified, more is probably better.

  • amhenk a year ago

    This is a neat idea. Are there examples of this in the wild?

    • timrogers a year ago

      Author here. We actually use deprecation headers in our GraphQL API.

bilalq a year ago

This is similar to the versioning scheme used by AWS and it worked well there. Versioning was rarely done, but this was a clean scheme for surfacing breaking changes to users. https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-...

It is a little surprising that the version defaults to the 2022-11-28 rather than defaulting to latest if omitted, but I guess that's a way to err on the side of not breaking old users when they upgrade an SDK.

  • timrogers a year ago

    Author here. You've hit the nail on the head on why we default to 2022-11-28 - it's all about protecting the hundreds of thousands of of existing GitHub integrations that rely on our current behavior.

nik736 a year ago

Can someone explain to me please how this is handled within the internal codebase. I assume that it's not a lot of if statements or copying whole API directories with every release but something much simpler? Thanks! :)

  • lnenad a year ago

    There's multiple of ways of doing this but let's assume some sort of routing model is used so you can have global rules based on the version provided that redirects/executes (depending on the layer where these rules are enforced) the proper code. From a code standpoint it can mean having a separate route group in your code for an API version, or it means that you have multiple routes for the "same" functionality per specified version, or it can mean something else entirely depending on how your code is structured. They most definitely aren't copy pasting parts of their project to support this.

  • timrogers a year ago

    Author here. We plan to publish a blog post in the next few months that explains how we do this under the hood. We have some pretty nice abstractions and tooling to make it as maintainable as possible.

j0057 a year ago

If you don't specify a version, the default will be the 2022-11-28 version, so naturally I wonder what happens in ~two years when that version gets retired.

  • timrogers a year ago

    Author here! We think that, most likely, we will end up defaulting to the oldest version. We’ll share specific plans on that when we announce our first version deprecation.

    Just so you know, we don’t intend to deprecate `2022-11-28` in two years-ish - we plan to keep versions around for much longer than that. But we offer a commitment of at least 2 years for peace of mind.

    • rattray a year ago

      Hey Tim, nice to see you here!

      You might consider behavior more like Stripe's here (disclaimer, former member of the stripe API platform team).

      As you're likely aware, Stripe defaults to the latest version for each user, and then stores that as the user's default API Version until they upgrade it at the account level.

      This works pretty well, and overall I'd recommend it. It may be better to give each API Key its own default API version, since different applications may expect different versions, and API keys can be a simple way to differentiate applications.

      Some of Stripe's client libraries also enforce sending a specific API version header to ensure the types are correct; my guess is that in practice, this is how most requests have their version dictated now, making the account-level setting a little anachronistic.

      Everything gets trickiest when it comes to webhook bodies, since the way the payload is rendered may be different across versions, so you can have one app create an object on version A and then a different app consume the webhook under version B and see the created object with a different shape. I'm not familiar enough with GitHub's API to know how best to handle this sort of thing.

      Anyway, good luck and happy to chat more.

      • leipert a year ago

        > It may be better to give each API Key its own default API version, since different applications may expect different versions, and API keys can be a simple way to differentiate applications

        That would not work if I (leipert) build a client against version 2022-11-28 and you (rattray) create an API token to use with that client on a later date though, right?

        • frosted-flakes a year ago

          You can manually set the API version associated with the token.

          • rattray a year ago

            Correct – and when creating the API Key, you'd want the API to give helpful hints about what other parts of the application use (which is trickier than it sounds).

            • leipert a year ago

              Ah. That makes sense. Similar to choosing scopes then.

        • easton a year ago

          It works for stripe because it’s one account per company, for GitHub you’d probably set it at the organization level, which would be a bit confusing for personal accounts as you say. I think you could still do it for personal accounts if you allow some kind of override.

    • mxscho a year ago

      The commitment to a 2 year grace period for deprecated API versions would only start at the release date of the succeeding API version, right? Many consumers are maybe only interested in the time they have to switch, not the general lifetime of a specific version.

    • LennyWhiteJr a year ago

      What does it mean for a field to be required in new API version but optional in the old version?

      • timrogers a year ago

        Author here! Are you thinking fields in a request or fields in a response?

    • pbreit a year ago

      2022-11-28 will never* go away.

      [*] never meaning a long time

plmpsu a year ago

Any reason for going with a header instead of media types?

I found media types convenient in that they can be explicitly specified in an OpenAPI specification.

Existenceblinks a year ago

I know this is not the point but this REST API is way better than their "UpdateEnterpriseAllowPrivateRepositoryForkingSettingInput" GraphQL type, dang!

I know it's not that REST .. it is what it is, probably annoyingly strict req/res, but it's very solid API. I'm glad they are trying to improve .. it.

CharlieDigital a year ago

Article says that it doesn't apply to the GQL API. Wonder what this means long term for the GQL interface.

  • timrogers a year ago

    Author here! We still love our GraphQL API, and REST and GraphQL continue to evolve in parallel.

    We’re just aware that GraphQL is pretty different, so if we want versioning there, we’ll need to tackle it differently. (On top of that, GraphQL has some nice primitives that come for free, like deprecated fields.)

    • the_mitsuhiko a year ago

      Would be curious to hear though how GitHub plans to evolve the GraphQL API. From the recent almost unannounced removal of the GraphQL package API one can get the impression that it’s safer to use the REST APIs.

      • timrogers a year ago

        Author here! At the moment, we allow teams across GitHub to make their own decisions on REST vs. GraphQL. Some teams are investing heavily in GraphQL (e.g. for the new GitHub Projects), whereas others are opting for REST. That said, we do want to firm up our guidance internally and communicate more clearly to the world how we think about REST and GraphQL.

        • the_mitsuhiko a year ago

          The inconsistency is quite annoying as a user. Particularly new projects is leaving a bad taste because its the only thing I’m aware if that’s GraphQL only and there is little communication about a REST API, yet the UI uses an internal REST API.

          • timrogers a year ago

            I definitely feel your pain and, unsurprisingly, it's something we hear very regularly from users. We're working on it.

karmakaze a year ago

Backporting non-breaking changes for new features should only go back a certain number of versions of date peroid. This gives some motivation to be up to date and when clients decide they must update to use a breaking change don't have as many breaking changes to adapt to.

borissk a year ago

Is this the way Microsoft and LinkedIn are going to version their public Web APIs in the future? I know Microsoft has a team that helps different groups unify the way they develop services, but wonder if that teams works with GitHub as well.

  • timrogers a year ago

    Author here. This versioning scheme was just developed within GitHub - so you shouldn't make any assumptions what this means for Microsoft's and LinkedIn's APIs.

stonelazy a year ago

Naming the versions literally as date itself was well thought out.

Existenceblinks a year ago

I'm not sure why don't we send diff(url) along with version in the header (if you have ever watched this https://www.youtube.com/watch?v=oyLBGkS5ICk Rich Hickey's talk)

Diff format example: {added: [{"path-to-key": {..}}], removed: [], changed: [{"a/b/c": {prev: {type: "string"}, current: {type: "number"}}], moved: [{old: stuff1, new: stuff2}]}

yewenjie a year ago

Is this model suitable for smaller projects? What does the development side look like for maintaining so many parallel versions?

  • timrogers a year ago

    Author here!

    Supporting many API versions in parallel definitely has an overhead from an engineering perspective - but, as the blog post says, we thought it was crucial that we unlock the ability for us to evolve our API whilst still providing consistency to existing integrators. Some form of versioning was the only way we could see to do that.

    We've built some pretty neat tooling and abstractions internally to make it easy for teams across GitHub to build and test multiple API versions. We hope to publish a blog post about that in the near future.

travisd a year ago

Since the author is responding to comments here: are there any changes coming soon that necessitated this?

  • timrogers a year ago

    Author here - you summoned me :)

    We have some small changes queued up, but nothing huge or earth-shattering.

    Our focus right now is on improving developer experience and consistency where we have some weirdness in our current API design that trips integrators up. Without versioning, we couldn't address those papercuts.

    We plan to release the first breaking version in the next few months.

indymike a year ago

Hmm. So what happens when two versions are released in one day?

  • layer8 a year ago

    They can hop timezones. There’s (almost) always two dates that are “today” depending on timezone.

  • macintux a year ago

    This is only for breaking changes, apparently, so presumably that situation won’t arise.

    • timrogers a year ago

      Author here. That's exactly our thinking - we are making the assumption that we won't want to release two versions with breaking changes on the same day. Who knows, maybe we will be proved wrong, but we think it's a safe bet.

      • macintux a year ago

        Thanks.

        I would be remiss if I didn’t take this opportunity to ask: the current API for retrieving commit metadata relies on git’s information about the committer, but you have much better information available to you. You know who actually pushed the commit.

        In an enterprise setting, that’s a big deal, because it’s easy for users to have misconfigurations. We have no shortage of commits in our repositories from ec2-user or root.

        I don’t think it makes sense to replace the information git supplies, but could you please augment it?

        • timrogers a year ago

          Could you drop me an email at timrogers at github dot com? I'd love to look into this.

          • macintux a year ago

            Sure, will dig a bit to make sure I remember the details and send you something, probably next week after I return from vacation. Thanks!

nesarkvechnep a year ago

Version in the URL and REST are oxymorons.

prepend a year ago

Sounds like GitHub just committed to only supporting their api for two years. That sucks.

I don’t think companies have to support forever but I don’t like the idea of having to rework my integrations every two years.

Also specifying a date is very clunky. I’d rather they use semver (like a non-barbarian) and stick with breaking changes going into a major release.

  • abraham a year ago

    > After two years, we reserve the right to retire a version, but in practice, we expect to support historic versions for significantly longer than this.

    Consider v3 has been around for a decade, they seems likely to last longer than 2 years.

    • prepend a year ago

      This is an example of when not to set a limit. There wasn’t a previous commitment to maintain v3, so why add one now? And why only 2?

      If they intend to maintain them for 10 years then don’t set a specific timeframe and just stick to whatever their current nebulous language is.

      • timrogers a year ago

        Author here. We just wanted to provide a guarantee to reassure users that the introduction of API versioning wasn't going to mean rapid deprecations of versions. We thought that giving a guaranteed minimum lifetime was a reasonable way to do that.

  • madeofpalk a year ago

    what's the difference between one string (a date) and other string (something vaguely resembling a number)?

    • prepend a year ago

      Semver contains info by looking at the values. 2.0 introduces breaking changes over 1.7.1.3. What does 2022-11-28 vs 2022-11-30 mean?

      Or how many releases are between 2022-11-28 and 2042-11-28? I’m sure there’s a docs page spelling it out with details, but being able to compare two versions and tell if your code will work is pretty handy.

      • madeofpalk a year ago

        They're only using versioning to indicate breaking changes though. There will never be minor or patch versions in this semver, only ever major (breaking) changes. They're saying "if the version is different in any way, your code might not work because there are breaking changes".

        How much the major version changed (v2 -> v3 vs v3 -> v9) doesn't actually tell you how many changes there were. In that way it's almost identical to using major-version-only semver, vs dates.

    • timrogers a year ago

      Author here. We went for dates because they allow you to see, at a glance, roughly how old the version you're using is. We didn't think that semver made sense because, in our versioning system, all new versions are breaking (i.e. major). Hope that makes sense!

  • Aeolun a year ago

    You’d only have to rework your code if you are using the API they changed.

  • aliqot a year ago

    show them who's boss and ask for your money back.

    • prepend a year ago

      I spend a lot of money on GitHub. And I use their api a decent amount. I won’t ask for my money back but it definitely makes me think more about GitLab and other competitors.

      • aliqot a year ago

        I was with you until Gitlab.

  • pbreit a year ago

    Current version has lasted for 10+ years but now that they hav versioning, might be gone in 2. Progress!

  • mberning a year ago

    This is the goofiest API versioning scheme I have ever heard of. Reminds me of opening a java project and finding stuff like joda-time-2004-11.jar.