One of the early discussions that popped up was a mail thread about versioning. Though the discussion is moving in the right direction (adopting the OSGi version model) the discussion was correct about the syntax but seem to assume that compatibility can be discussed bi-laterally. I am afraid that they are not alone, most people assume that there are only two parties involved with versioning (though in the case of marketing versions the assumption is just one party). However, the key insight we had in the OSGi was that it is a tri-lateral problem.
It is a tri-lateral problem because today interface based programming is not only very popular, it is a best practice. They key advantage of interface based programming is that the Provider and Consumer are bound to an API (the interface) but not to each other. That is, interface based programming removes implementation dependencies. However, though this model significantly reduces the coupling, it unfortunately complicates the versioning in a rather subtle way. So subtle that its implications are ill understood.
With interface based programming there are 3 independent parties:
- C - Consumer
- A - API
- P - Provider
So how do we version this? Versions are a mechanism to communicate the evolution path of an entity. If I have an entity X that depends on entity Y, how do I decide that Y is compatible with X's assumptions? If X is a Consumer, backward compatibility is relatively easy to provide. If X is a Provider, backward compatibility is much more restricted. We therefore need to encode the following cases in a version:
- Backward compatibility with all
- Backward compatibility with Consumers
- Backward compatibility with Providers
- micro - Changes in the micro part do not affect Consumers nor Providers
- minor - Changes in the minor part affect Providers but not Consumers
- major - Changes in the major part affect Providers and Consumers
The only thing now left is the granularity of the version. Do we put the version on the JAR (bundle) or the interface class? If the JAR contained only API then the JAR would be a good granularity. However, in practice JARs are often not very cohesive and carry besides their API also implementation classes and convenience dependencies. That is, their fan out is uncomfortably large causing maven to download the internet. Classes are too small because most API's consist of a set of classes and versioning these separately is not sensible. The perfect granularity to depend on is therefore the package. A package is a cohesive set of classes that has therefore the right granularity for an API.
I hope that this blog clarifies why it is important to understand versioning in the light of modern programming.
Peter Kriens
Hi Peter,
ReplyDeleteI am quite convinced too than proper versioning can help giving confidence when updating libraries.
You might be interested by a small Java library I created to help applying semver principles (http://semver.org) to Java project. It also includes a maven plugin to check for backward compatibility during maven lifecycle.
See https://github.com/jeluard/semantic-versioning (documentation http://jeluard.github.com/semantic-versioning).
Julien
Interesting, though the semantic versioning paper you reference does not make the distinction between the consumer and provider of an API that I tried to explain in this blog. Notice also the work in bnd and in bndtools.
ReplyDeleteRight. I introduced a similar concept in my API (based on work done by bndtools guys).
ReplyDeleteSee Checker#CompatibilityType.