Need someone to lead product management at your software company? I create software for people that create software and I'm looking for my next opportunity. Check out my resume and get in touch.

Versioning web services

Freshness Warning
This blog post is over 18 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

Too Big To Fail (Apr 9)
When a company piles resources on a new product idea, it doesn't have room to fail. That keeps it from succeeding.
Go small (Apr 4)
The strengths of a large organization are the opposite of what makes innovation work. Starting something new requires that you start with a small team.
Start with a Belief (Apr 1)
You can't use data to build products unless you start with a hypothesis.
Mastery doesn’t come from perfect planning (Dec 21)
In a ceramics class, one group focused on a single perfect dish, while another made many with no quality focus. The result? A lesson in the value of practice over perfection.
The Dark Side of Input Metrics (Nov 27)
Using input metrics in the wrong way can cause unexpected behaviors, stifled creativity, and micromanagement.
Reframe How You Think About Users of your Internal Platform (Nov 13)
Changing from "Customers" to "Partners" will give you a better perspective on internal product development.
Measuring Feature success (Oct 17)
You're building features to solve problems. If you don't know what success looks like, how did you decide on that feature at all?
How I use OKRs (Oct 13)
A description of how I use OKRs to guide a team, written so I can send to future teams.

Older...

What I'm Reading

Contact

Adam Kalsey

+1 916 600 2497

Resume

Public Key

© 1999-2024 Adam Kalsey.