Versioning web services

Freshness Warning
This blog post is over 17 years old. It's possible that the information you read below isn't current and the links no longer work.

When I add a new feature to Tagyu’s web service API, how should I best version it? I don’t want to maintain and support multiple, old copies of the API, but I also need to ensure I don’t break backward compatibility. A change to the API can’t cause existing apps out there to stop working.

At first I thought this was an easy problem. I’d simply ensure that any time I made a change to the API the changes would be additive. I wouldn’t remove any data, I wouldn’t change the location of representation of the data, I’d only add new data. If a case came up where I needed to change how the XML data is stored, then I’d consider maintaining parallel versions of the API for a period of time. I’d figure out how to do that when the time came. Amazon Web Services inserts their API version into the URL, but that’s always seemed a bit clunky to me.

Today’s new API feature made me rethink how I change the API and represent different versions. It made me rethink what changes should be even considered as a new version. Today’s change involved the insertion of a couple of attributes to one of the XML tags. Shouldn’t be a problem right? That’s what I thought, too.

Before the change, the XML node for a suggested tag looked like this:

<tag>sometag</tag>

After the change, it looked like this:

<tag rel="related" href="http://tagyu.com/api/tag/sometag">
	sometag
</tag>

Those two new attributes broke some APi implementations, including my own software, the Tagyu Movable Type plugin. The plugin uses XML::Simple to parse the API’s response. And as far as XML::Simple is concerned, those two formats are completely different XML structures. XML simple represents the first one as this…

{
    'tag' => 'sometag'
}

And the second as this …

{
	'tag' => {
	 	'rel' => 'related',
		'href' => 'http://tagyu.com/api/tag/sometag'
		'content' => 'sometag'
	 }
}

The Tagyu plugin accesses the tag through $tags->{'tag'} but under the new structure, that’s not the tag at all, it’s a hash that contains the tag, among other things.

The point of all this is that even though I thought I was being careful, today’s API change broke things. And that’s bad. It should never happen.

I’m looking for thoughts on how to version a web services API or really any software that’s delivered as a service. How do you prevent changes from having adverse effects while at the same time providing improvements. Is different API versions at different URLs really best or only way to go?

Michael Buckbee
February 14, 2006 2:57 PM

Trying to find the the "best" solution is always a tough one, but I think the simplest (by which I mean the one most guaranteed to not break some downstream application) is the one you suggested: versioned URL's. Any other method I can think of (additional input variables, an application registry so you "know" when somebody signed up and are dynamically feeding them the right one, etc) strikes me as very non-discoverable and fragile.

Byrne Reese
February 14, 2006 3:10 PM

Here are the methods I am familar with to version APIs: * Place version number as an argument/parameter (this is done in LiveJournal's XMLRPC API) and a few others that I am aware of. The parameter must be optional and must default to the most recent version. (SOAP does this, and Grand Central did this) * Place version number in the declared namespace for the XML document/response. The namespace would only need to be changed for any non-backwards compatible change (e.g. renaming an element) * Place version number in the URL and create separate endpoints (Amazon does this as you point out) I myself tend to favor Amazon's approach because I personally find it easier to maintain. If you rev your protocol a lot (which if one is doing, should realize is a problem) trying to handle all the various interface versions in a single code base is cumbersome at best. Plus when it comes time to discontinue a deprecated API then you just expire the URL. What is easier then that. But vendors should architect their APIs, just as the architect their web UIs: clearly abstract form and function. If they do that properly then it really doesn't matter what technique you use provided that it is very well documented. That and they should put a lot of thought into their API prior to releasing it, and avoid changing the interface too frequently.

Colin D. Devroe
February 14, 2006 4:51 PM

I agree with Byrne: the choice is yours. One way I've done it, is to actually have the url represent large version upgrades. I've only had to put together a few APIs and it was not used on a grand scale (such as Amazons), but this worked out fairly well. Small revisions, meaning those that would not effect any of those using the API, were done by simply showing the version within the XML. This way those using it could chose to not use a specific build if they so chose. However, for larger version upgrades I would literally allow those that chose to change the calling URL. api1.domain.com (example, not really what I ended up using), api2 and so on. The rest was handled by .htaccess mod_rewrite rules which would end up simply changing another URL arg like version=1, or whatever. This way, those using the API did not have to change any of their argument calls (which if their application is sloppy could be hundreds of changed lines), but only their major call. I hope that made any sense at all, I haven't eaten yet today.

Adam Kalsey
February 14, 2006 6:39 PM

The thing is, I thought I _had_ thought out the API. I'd made plans for hwo to grow it slowly without making wholesale changes to it. What I failed to account for was some of the idiosyncracies of parsers. XML::Simple by default transposed the XML into an odd structure. So it's now obvious to me that even simple changes aren't so simple. I don't really like including version numbers in the URL for a number of reasons. First off, it exposes internal administrivia. The developer shouldn't have to care what version of the API they're using. Secondly, it doesn't seem like a very RESTful thing to do. What's more RESTful? example.com/product/1234 or example.com/v1/product/1234

Colin D. Devroe
February 16, 2006 6:30 AM

Adam: I agree totally. However, if you have a user-base, and an upgrade to an API will "break" whatever your current base has developed, somehow making the distinction between versions is absolutely vital.

Vincent D Murphy
February 18, 2006 8:14 AM

One thing which I think would help, if only on a 'social' level, is to offer a feed of all API changes. That way users know to re-run their tests, and hopefully even catch up. The XML::Simple behaviour is interesting; a good use case, and an example motivating the use of versioning. I would prefer a module which complicates the simple case in order to have an attribute be just an extra hash entry. By always having a 'content' hash entry, at the cost of an extra dereference in the simple case, for example. Less Magic. From a REST perspective, the changes in your example are to the representation format, not the application protocol, and the software using the representations (XML::Simple) seems a bit too brittle to cope with such a simple, additive change. (I know much of what I'm saying assumes an ideal world, but I am coming at this from an architectural level of abstraction :) )

Chris Lihosit
March 16, 2006 11:20 AM

This discussion makes me wonder: If the whole notion of using the REST design pattern lay with 1 URI == 1 Resource, then doesn't changing the URI to reflect a difference in XML reduce the effectiveness of the whole pattern in that duplication of information (with little benefit) is inevitable? I'd categorize the XML::Simple behavior as a bug, since in either case of the example XML node above "sometag" is most certainly content to the element. On your later post, really like the concept of a versioning system, that returns latest, past and future nodes, but really isn't that exchanging the practice of [some URI]/v1/[more URI] with just a more verbose "v1" ? Love to hear your thoughts.

This discussion has been closed.

Recently Written

Input metrics lead to outcomes (Sep 1)
An easy to understand example of using input metrics to track progress toward an outcome.
Lagging Outcomes (Aug 22)
Long-term things often end up off a team's goals because they can't see how to define measurable outcomes for them. Here's how to solve that.
Tyranny of Outcomes (Aug 19)
An extreme focus on outcomes can have an undesired effect on product teams.
The Trap of The Sales-Led Product (Dec 10)
It’s not a winning way to build a product company.
The Hidden Cost of Custom Customer Features (Dec 7)
One-off features will cost you more than you think and make your customers unhappy.
Domain expertise in Product Management (Nov 16)
When you're hiring software product managers, hire for product management skills. Looking for domain experts will reduce the pool of people you can hire and might just be worse for your product.
Strategy Means Saying No (Oct 27)
An oft-overlooked aspect of strategy is to define what you are not doing. There are lots of adjacent problems you can attack. Strategy means defining which ones you will ignore.
Understanding vision, strategy, and execution (Oct 24)
Vision is what you're trying to do. Strategy is broad strokes on how you'll get there. Execution is the tasks you complete to complete the strategy.

Older...

What I'm Reading

Contact

Adam Kalsey

+1 916 600 2497

Resume

Public Key

© 1999-2023 Adam Kalsey.