Versioning is Hard

about | archive

[ 2007-October-17 22:04 ]

I recently watched a video about Java modules, a plan to attempt to improve the easy of use of Java "components." The gist of the plan, as I understand it, is to standardize the installation process of .jar files (read: DLLs), and then to standardize the process of declaring and importing dependencies, including versions. While this plan may be better than the status quo of ad-hoc .jar dependencies, I do not think this is a great leap forward. I think this looks a lot like how DLLs and COM work on Windows, and I don't think that solution has worked very well.

In my opinion, the critical assumption that the authors of this plan make is that version numbers are useful for determining which libraries are compatible, and which are not. For example, if I declare a dependency on foo.jar, version [1.1, 2.0), it means that I'm willing to link against any version of foo.jar between version 1.1, and up to but not including version 2.0 (e.g. version 1.99.42 is compatible, but version 2.0.2 is not). The problem is that I need to be able to see into the future to determine if the maintainer of foo.jar will do the "right thing" when releasing a new version of their library. Additionally, the maintainer of foo.jar needs to decide if their changes will break applications. This is actually extremely difficult.

For example, let's imagine that foo.jar contains a small bug in a rarely used chunk of code. Should this be an incremental +0.1 release, so that existing applications get the bug fix? Or should this be a significant +1.0 release, since an application that was designed against the buggy version might actually be depending on the bug to function correctly? Basically, this question is impossible to answer. Some applications will work better with the bug fix, and some might work worse. The correct answer depends on the situation.

The end user just wants the application to "work." While in some cases they might want bug fixes, security fixes, and other updates, above all they want to use the application. Any changes risk breaking things. My preference would be to only pick up a new version when the application explicitly says that the new version is acceptable. This behavior is possible with the Java module system, but it is not the default. This conservative policy would attempt to ensure that if applications work when they are tested, and continue to work after they are installed on the user's system. I think automatically picking up the latest and greatest version of a jar is going to cause more problems than it solves.

Even better would be for someone to come up with good ways to automatically "version" dependencies. For example, one naive technique would be for an application to declare "here are the external classes/methods I use." The module system could then compare this list with the APIs that the module actually exports. If the API changes, you can't use those two modules together. However, if a version of the jar changes an API that the application does not use, that would be considered a compatible change. This simple technique does not detect situations where the implementation of the API is incompatible, but perhaps some combination of static and dynamic analysis could assist here. This is, sadly, a research problem, so I understand that Java is going to adopt a more conventional technique. I just wish there were better solutions out there.