If you work with generic media types like plain XML, JSON and(X)HTML (and others) then there is absolutely no reason to version those through the content-type and content negotiation. I mean, when did the processing rules for XML and JSON last change? Oh, well, the rules for HTML does change - but this is not handled through the media type, but rather through a versioning mechanism inside the payload (the DOCTYPE).
"Unfortunately" there is a lot of domain specific media types out there - and since domains changes regularly over time, well, you start thinking that versioning must be done through the media type. And this is where things start to break down. If you avoid domain specific media types (like vnd.order+xml and so on), then you can avoid versioning through the media type.
But, of course, your application still evolves over time. Then what?
- Do not expose your internal business objects directly on the wire (for instance by JSON serializing them directly). Do wrap your business objects in a (more) stable external format, such that you are free to change the underlying business objects without the public facing format changing too. Keep that transformation in sync with your internal changes.
- Don't make breaking changes to your public format.
- Add versioning information inside the payload.
- If you have to introduce breaking changes then create a new resource for the new version.
- Put links to never versions of the resource into the original resource (for instance using the Link header). Never clients can look for these links and move on to the new and improved version.
- Introduce new links, in addition to the old ones, where other resources are linking to your resource (if possible), using new link relation names or similar mechanism. Newer clients will know which of the links to follow - older clients will continue with the link that they have always used.
- URLs are not identities for your business data - they are identities for various representations of your business data. I mean, api-v1.example.com/person/1234
is obviously not the same resource as api-v2.example.com/person/1234
- but it can certainly represent exactly the same piece of business data.
(and, yes, this is quite the opposite of what I argued in my first post in this thread)