Versioning in software is as old as software itself. So, one should assume we are able to handle versions pretty good right? Well, you can’t be more wrong. Versions at the moment – and this holds true for ALL programming languages and concepts I know off – haven’t even remotely been done right till now. You ask why? Well, that’s easy to explain…
Currently, versions can only be considered as simple timestamps with a sophisticated syntax. You might be able to read intentions from the provider but further than that they are useless. The syntax of a version theme might indicate some sort of intend to express compatibility, but in fact this is never enforced, so you can’t really trust it! You ALWAYS have to check it yourself (manually or with integration tests) to figure out if it really fulfills the contract. As a result you can only be sure to some extend that some bits changed, if the version got increased – what ever this means. For me, this is the ultimate problem with dynamism in applications. You can’t test all possible installation scenarios of your clients as soon as you open yourself to third parties. Someone will break your perfect little world, better anticipate it!
In OSGi, things get even harder from a testers perspective (and developer for that matter). As the only system, I know of, it defines a way to describe future versions a component (aka bundle) can be compatible against. For instance you can specify that you require a certain version of a package your module depends up-on. Therefore you just add the required dependency in your Manifest.mf file as a range:
A typical usage scenario in OSGi might look like this:
Here the dictionary "user" (cl.client) defines a usage dependency of the dictionary API (dictionary.api). As you can see, the dependency definition differs between a "user" and an "implementer", because the implementer of the functionality (dictionary.impl) not only has to be binary compatible, but also source code compatible (like implementing a new method of an interface). Fortunately the OSGi allows to at least express such a dependency. The distinct version scheme of OSGi imposes a specific usage. If you're not binary compatible bump up the major number, if you're binary compatible bump the minor number. If you do bug fixing, not affecting the API go for the build number and last but not least, if you want to indicate a distinct notion like a release or an iteration, use the quantifier. That's what it should look like:
You may think this is a need approach and you're probably right. It is great compared to everything else we have right now. Unfortunately this is not the silver bullet. Not at all, but it is a scheme which is explicit and well defined to some extend. Someone reading this, will at least have an idea what the intention of the component author was.
Now, what is the catch. Plain simple, this versioning scheme is not enforced. Everyone can version their OSGi artifacts however they want. There is no enforcement whatsoever. When you go to any bundle repository, none of them are verified and frankly, I don't know how they ever will be. Of course, we can go all fancy and do API checks as Eclipse does (and by the way, which is doing a pretty good job so far) or use an approach introduced by Premek Brada at the last OSGi DevCon in Zurich. There is even the possibility to rerun all the former unit tests of a bundle that should be compatible with the latest version to check for semantic inconsistencies, but who is doing this and who guarantees that this covers all edge cases? No one can! Well, at least yet. Maybe in some years with fancy self learning AI on quantum computers, but for now we have to stick what we got.
I understand, my post is quite dark and negative up to now - nothing will work, we will ultimately fail, software reuse as the epic fail, blah blah blah. It's true, we are not going to be a 100% perfect, but maybe, we don't need to. Maybe we should just take what we got and try to make the best out of it. Evolve slowly but consistently and fail gracefully...
For one, testing is inevitable. For instance take the small and innocent build number that indicates just a bugfix. Now what is a bugfix? It can be a performance improvement - ok, that fine. It can also be a miscalculation or returning true instead of false in some odd case. When developing against such an behavior it is certainly not a compatible change, when you expect such a behavior and it gets fixed in a build number increased release. On the other hand, bumping the major number, because it in fact affects the client is a pretty heavy step, rendering all users incompatible unless they bump the version range to a higher number. The only thing in my opinion is creating test code to check if this behavior continues to be there and implement ways in your code to handle the fact that at some point it is different. This is painful, but there will always be this one edge case screwing with you.
Another thing we can do is rethinking of the actual version scheme. For one, this bump in the minor or major version, does it actually affect the implementer of the API? Well, not necessarily - not in all cases. So why do we start from 0 every time a higher number gets increased? Aren't they distinct from each other? Well, maybe they have to get increased by one, but why reset them back to zero?
2.4.2 becomes 3.5.3 or 3.4.2 instead of 3.0.0.
The same holds true to version ranges. Why is the consumer in charge of defining the range? Ultimately, the provider knows what version the current library is still compatible with (from a client point of view). Now, having both, the client and provider version range can allow for a very flexible system.
Last but not least, the ranges we're defining, if a version segment gets increased independently from each other, as explained before, should version ranges then incorporate this as well? Allowing ranges on all version segments, not only as a whole?
I know, I am describing a maintenance nightmare. How could one stay sane with so many variants? Short answer, humans can't! Fortunately, there are tools and best practices that can help. With tooling you can get the version right you can depend upon. This is already done. With approaches like running old unit tests against the new API that specifically aim at the API level can verify the (most) of the behavior didn't change. It is not easy - no doubt, but it is a chance we should take. At least when we want to get to a point where we actually can reuse 3rd party components.
Truth to be told, most of the things I said are just idea's I picked up over the last few years and there is currently a lot of discussion going on.
- Peter Kriens: http://www.osgi.org/blog/2009/12/versions.html
- Chris Aniszczyk: http://aniszczyk.org/2009/11/23/jigsaw-versioning-is-ridiculous/
- Neil Bartlett: http://neilbartlett.name/blog/2009/12/11/reasons-not-to-mourn-jsr-294/
- "Sun" Versioning: http://wikis.sun.com/display/IpsBestPractices/Packaging+Best+Practices+-+Versioning
- Alex Blewitt: http://alblue.blogspot.com/2008/05/version-numbers-and-jsr277.html and http://alblue.blogspot.com/2009/12/jsr294-is-dead-long-live-osgi.html
- Mirko Jahn (myself - sorry): http://osgi.mjahn.net/2009/04/02/the-myth-of-software-reuse/
If you want to get a full picture, I urge you to read through all of those sources, because ultimately, the worst thing we can do is redoing the mistakes of our past!
 PDE API Tools: http://www.eclipse.org/pde/pde-api-tools/
 Safe Bundle Updates: http://www.slideshare.net/pbrada/safe-bundle-updates
Update: I added the links Alex mentioned. I certainly read his blog, which eventually influenced me as well. None of these concepts are coming from me personally unfortunately. I can't take any credit for it. Sorry for not giving you the credit you deserved in the first place, Alex. Btw., the order is arbitrary coming from the top of my head. No semantics are applied whatsoever! I hope I haven't forgotten someone else as well!