Saturday, December 12, 2009

Versions

Looking at the raging debate at versions again I can't help but feeling that there are many people out there that do not understand what OSGi versions are, or what versions are intended to achieve in general. It is not the syntax that are important, it is about the standardization of the semantics.

Versions are a language designed to let two parties communicate over the barrier of time. It is like a Domain Specific Language between artifacts that evolve over time. By having fixed version syntax and semantics, an importer can express how it feels about future changes in the exporter. These semantics tell the exporter what version to use when it evolves. The version indicates to tools how different importers and exporters an be combined in a system.

The simplest solution to versioning is to use a fixed identity. A imports B version 1. If B changes, it becomes version 2 and A must be recompiled, but this changes A so it must also increment its version, ad nauseum. Such systems only work when all the software is in a single build integrated with the deployment. Every deploy is then based upon a full new build, you're basically always at the latest version. However, when you use third party packages or you sell your software then latest version systems tends to be unworkable because minute changes ripple through the whole system.

We need some oil to ease the friction, meet version ranges. A version range allows A to import B from version 1 to version 2, non inclusive. This way we can allow B to increase it is version from 1.1, 1.2, 1.3, etc. without requiring A to be recompiled, stopping any rippling effects dead in their tracks. By specifying an import range of [1,2) A relies on B to properly version future releases. If a change is backward compatible, B can increase the minor part of the version (second number) but does not have to increase the major number (the first number). If a change is made that would break existing code, then B increases the major number and resets the minor number. Such a breaking change would get version 2.0.

We skimped a bit on backward compatibility, this is not a well defined concept and partly in the eye of the beholder. A new method on an interface is backward compatible for code using that interface but breaks an implementer of that interface. How is this difference handled? Well, we can put the semantics on the minor part of the version. Implementers use a range for a single minor version and not a single major version number. So if A implemented interfaces from B it would not use [1,2) but [1.0,1.1), if it only used the interfaces in B it would use [1,2).

Backward compatible does not mean forward compatible, if A was compiled against 1.4 then all bets are off if it was bound to version 1.4. It is therefore important that A requires the base version it was compiled against as a minimum. That is, [1.4,2).

However, we make many changes that we want to deploy but we do not want this reflected in the dependency. If we force all dependencies to be the latest version we end up with the same situation we had when we had a single number; any change in version ripples through the whole system again. That is why the OSGi version has a micro number, the last part. It indicates that you made a change but this change does not affect any clients. It is a small bug fix or minute functional enhancement. Obviously deployments should have the latest fixes installed but it is not enforced to keep things manageable in the field. So even though A might be recompiled against 1.4.2, it would put out a version range that did not include the micro part: [1.4,2).

Last, and in this case also least, there is often a need to know which of two artifacts is the newest, or has a certain state. This is left to the qualifier.

From the previous description it is clear that a simple version syntax can have surprisingly rich semantics. Having these semantics specified and agreed upon allows tools to automate parts of the process of maintaining the versions of the artifacts. This is a good thing because users are quite horrible in maintaining versions. For example, the bnd tool can automatically set the import range depending on the export range, takes implements versus uses into account, and I am working on automatically calculating export version changes based on a previous release. Also Eclipse PDE has extensive versioning support, and I expect bundlor also to do clever things. And last, but not least, it looks like Sonatype will also standardize on OSGi version. Without having fixed syntax and semantics, all these tools would have to use proprietary mechanisms.

So yes, it can be fun to use π for version 3.14 but it kills any hope of tools that understand the semantics and take the error prone chore of maintaining versions out of our hands.

Peter Kriens

11 comments:

  1. By 'sonatype', you mean for Maven versioning purposes, correct?

    ReplyDelete
  2. I think he means to say that Sonatype will help Maven standardize on OSGi :-)

    ReplyDelete
  3. Well said Peter. I blogged about my opinion of Jigsaw versioning awhile ago but you have said what I meant in a more eloquent fashion.

    In regards to Eclipse, I think one of the reasons the system has held together is due to our strict versioning guidelines and the tools we have developed to manage those versions. Once you start to build a large modular system like Eclipse, the maintenance of those versions becomes a critical task. I think the Jigsaw team is new to this and doesn't realize the impact versions have when it comes to modularity. That is my opinion at least.

    You should blog more by the way.

    ReplyDelete
  4. Good story. I think it's unlikely that the whole world will agree on the same versioning semantics. Therefore, in practice you will probably still need to study each third party library, ask their development team about the semantics they use and take that into account when using the library.

    You can either build a knowledge base with such semantic meta data, or even simply use tooling to, upon usage of each new version of a library, audit its changes and assign a new (bundle symbolic name and) version yourself.

    ReplyDelete
  5. @AlBlue & @Jason van Zyl:
    Yes, Jason is correct.

    @Chris:
    I'd like to blog more but there are two issues. Time is one, the other is that the objects that interest me are often under discussion inside the OSGi and I can't take that discussion outside.

    @Marrs:
    I do not ask the whole world to agree on the same version semantics. The OSGi world already did and I think it shows it magic in the available tools. I think this is becoming on one of the primary advantages of OSGi.

    ReplyDelete
  6. One thought I had is that... the current OSGi specification is a bit soft on how to handle versioning. There's a brief mention of a sample policy in the core specification... would there be any benefit in tightening this down a bit? If we don't want to choose a policy, how about expanding the section to talk about the importance of versioning along the lines of what you blogged about?

    ReplyDelete
  7. Peter, has there been any development on "export ranges"?

    Last year, you wrote: "As an aside, I believe life would have been easier if we would have had export ranges because the exporter of a package knows exactly to which version he is compatible, being the responsible person for that package. If he changes a package, he is the one thinking and deciding about backward compatibility. The importer is almost always guessing. That is, if I make a change to a package called p, I can knowingly say that I am package p version 2, but I am completely compatible to version 1 I therefore can export p;[1,2]. In contrast, if I import package p I have no clue if version 1 and 2 will be compatible except for the convention of version number interpretation. Alas, export versions did not make it to R4."

    http://www.osgi.org/blog/2008/02/research-challenges-for-osgi.html

    ReplyDelete
  8. @Chris:
    Yes, I'd like to tighten up this version policy. I file an internal issue on this. In bnd, I have 3 programmable version policies.

    @luke:
    I still believe export version ranges/collections on are a thrilling idea. We seriously discussed adding them to OSGi v4.2, however, we found out you could already do it. You can export the same package multiple times with different attributes. Though this in the current model is not very convenient, I did not push it too hard because I think the industry is in a flux at the moment for this subject. And I strongly believe a standard should follow by encoding best practices and not lead with untested ideas.

    I put out the idea of export as a research challenge because we need to more about this, but so far nobody has reacted. Though I've talked to several people that are doing research in this area.

    That said, the recommended OSGi versions with the programmable version policies in bnd seem to be doing a reasonable job within my current understanding. Sigh. Wish I had time to work more on this.

    Peter Kriens

    ReplyDelete
  9. Hi Peter,

    interesting post! I am glad to read that there is still progress going on with regard to versioning. I was wondering if the BoF at the OSGiDevCon had triggered some "thoughts" in that respect.
    You mentioned it is possible to export several versions of a package, what about idea of treating the different version segments independently? As I recall it, it was your idea...

    Cheers,
    Mirko

    ReplyDelete
  10. Ah, but is it flexible enough to be able to support Roman Numerals :-)

    ReplyDelete
  11. Peter, you said:
    "I still believe export version ranges/collections on are a thrilling idea. ... we found out you could already do it. You can export the same package multiple times with different attributes."

    I think I see what you are saying now, cause I had to use this capability recently.

    I had one bundle (A) that imported a specific version of a package from another bundle (B) whose authors didn't specify export package version attributes. My workaround was to add a fragment to B that simply contained a manifest which exported a versioned package. At runtime, I guess that means that B is exporting two "versions" of the same package. Is that the sort of solution you were describing?

    ReplyDelete