4 ms·
I have to disagree somewhat: versioning objects or endpoints is never the right solution. I'd go so far as to say that versioning the whole API is simply the le
by cletus 6y ago
I have to disagree somewhat: versioning objects or endpoints is never the right solution. I'd go so far as to say that versioning the whole API is simply the least bad option.
What constitutes a major version? Simple: as soon as existing clients break it's a new major version. That means you can, for example, add new endpoints because that's not a breaking change. You simply can't remove or change any of the existing endpoints, fields or objects.
It also means that a client can't mix-and-match what major versions to use ie you can't use /v1/customer and /v2/account. Why? Because clients will do that as a quick hack and you need to save them from themselves.
So why not version endpoints or objects? Because of environment bifurcation. Let's say you have 2 API versions (v1 and v2). To verify your API you can test each independently. That's relatively easy.
But imagine you have 10 endpoints and each is versioned separately. Now you have to test 2^10 possibilities. Clients will do stupid things like use a different version as a quick hack or even when they don't need to and you'll be debugging those things forever. Don't give your API users footguns.
- hinkley 6y agoOne very powerful way to socially engineer people into upgrading to the new API is to make sure that the newest one is the most efficient. Too often, what happens is that version 1 is 'faster than possible' - it ignores some sanity checks that turn out to be important, and so the new version has more logic in it, making it 'slower'. This fact lets people rationalize that they should stay with the oldest version of the API that is still available, but also results in preemption every time you retire a deprecated API. And they will likely 'blame' this interruption on you. If instead the old API becomes a wrapper around the new API, now the oldest API is the slowest, because it bears both the costs of all of the sanity checks and the thunking layer. Now the motivation is to upgrade sooner. Essentially you have 'migrations' for you API. Yes, this constrains the sorts of changes you can make in some dimensions, but being able to accelerate retirement of old APIs I feel 'pays for itself' by unsticking you in other dimensions.
- ghoward 6y agoI had some of the same thoughts reading the article, but I could not articulate them as well as you did. A good resource for the details about what constitutes a breaking change is Rich Hickey's talk about Clojure's Spec. [1] [1]: https://youtube.com/watch?v=oyLBGkS5ICk https://youtube.com/watch?v=oyLBGkS5ICk
- mumblemumble 6y agoMy thoughts exactly. There may be no "right way," but I'm pretty sure there's a least wrong way.
- closeparen 6y agoThese 2^N problems are really under appreciated, especially with experiments and feature flags. We ship an app with tens of thousands of flags! And chide each other for hacky, irresponsible engineering if we ever neglect to multiply the number of configuration states by two when releasing a new change. What percentage of the configuration space is actually tested? Perhaps one in a billion.
- vsareto 6y agoAren't you supposed to remove feature flag code after it's found to be stable? Some products block paid features with flags and those stay in, of course. Do others get actual dev time to clear that up, or is it just rolled up into any technical debt work?
- deckard1 6y ago> Do others get actual dev time to clear that up Depends on how much influence your PM has over your team and whether your team really cares about going back and cleaning it up or would they rather go and do resume-building features instead. I feel like we already know the answer to this. I've often felt that all PMs should be required to take a class on combinatorics before being allowed to add a single new feature flag. Of course, devs spend countless hours unit testing and going through QA and then some marketing dickbag can come along and stuff a random script tag into the website via Google Tag Manager and destroy the performance or even kill the site entirely. Yeah, I'm a little bit cynical these days.
- closeparen 6y agoAFAIK there is automated cleanups for flags abandoned in the "off" position but "on" flags are forever, in case the feature ever needs to be turned off in the future. In theory this promotes reliability, but I think it's rarely understood what non-local effects there might be if a given flag is turned off, and the more time goes on, the less we understand it.
- lazyasciiart 6y agoHa. I'm trying to convince people to delete flags that were released in 2015.
- noalarmsplease 6y agoI don't know if it is industry standard, but it would be good if a user session (ie. bearer token, session id, cookie) was valid only on the designated API version (no mixed usage among different versions using the same token). The user then would have to manage distinct tokens if he decided to use two versions of the same API simultaneously.
- iudqnolq 6y agoWhy? That just sounds annoying. I don't like making things annoying to disincentive users from doing them, people tend to use legacy APIs when they have a good reason to need to, and the inconveniences just make them sad.
- UncleMeat 6y ago> as soon as existing clients break it's a new major version How do you know when this happens? You don't have integration tests with all of your clients. What if they break because of some insane dependency on your internal behavior. Let's say that they rely on your API taking >100ms to respond in all cases and you optimize things so now it returns in 50ms most of the time. Is that a breaking change? Clients will always always always find a way to depend on every single observable behavior of your system. This makes everything a breaking change, given enough users.
- hinkley 6y agoIf you need to add a new field to your API, you probably have to add that field to your data model. So you start off by adding the field but not expecting any data. You might even ignore it. In order to do a migration, you are going to need a default value for that field, so you start storing the default value. You migrate existing records to contain the default. Then you start validating and storing the supplied value, with the default if it's missing. Next you add warnings to the defaulting logic. Eventually that warning becomes an error. Only near the end of this process can the rest of your code start making decisions based off of the new value instead of the old one (except in sanity checking exercises you perform along the way to verify that this is a choice and not a bug). Once that is done, you might start removing or ignoring the old value. What I think is often lost in technical discussions about APIs is that the above process has built into it a framework for talking to your customers about the change, whereas often the subtext of these migrations is "how can I use technology to avoid having to talk to people". Changing relationships without having an adult conversation with people is passive-aggressive behavior. You have a technical problem that has become a social problem. You can't fix it with technical solutions alone. Sack up and talk to people. Even, or perhaps especially, if you have a solution that requires little to no work on their part. Abstractions leak, people hate nasty surprises, and their follow-on questions most likely will bring up cases you never knew about or simply forgot. Having automation makes this conversation easier, but it doesn't obviate it.
- audron 6y agoYou gotta draw a line somewhere and I'd probably draw it in proximity to the documented behavior, and apply some superheated sanity to it while at it. Every change ever will upset somebody. And at the point where you have enough users you have better stuff to care about than some people that natural selection will get to sooner than later.
- iudqnolq 6y agoThat's pretty much what the author has to say > Resource Versioning (URI-based) > Versioning specific resources by theirselves. > In the same example of /api/v1/companies and /api/v1/users, if companies changed substantially then maybe a /api/v2/companies is created, but /api/v1/users remains untouched. This makes upgrades easier for clients as they know where to focus their attention. If there is no v2 for users, they just keep on using the v1. > One problem with this approach is it looks nearly identical to global versioning on the outside, and isn’t. [goes on to list more problems, including MxN]