Brian's Waste of Time

Mon, 23 Jun 2008

Library Versioning, Redux

I am a big fan of the APR versioning guidelines, but there is an element they don't capture well somewhere between major (backwards incompat change) and minor (forward incompat change) in Java. If you follow the, generally recommended practice of exposing things via interfaces (pure virtual classes), you have opened the door for users to implement those interfaces.

In a C-style world, adding a function to a library would bump you from 1.6 to 1.7, using APR guidelines. In an interface-driven Java-style world, adding a method to an interface would bump you from 1.6 to 2.0. Or would it?

To take a concrete example, a coworker (thanks Jax!) recently re-added first class support for callable statements to jDBI. jDBI uses a Handle interface to expose operations against a database. It has gained a method:

public <ReturnType> Call<ReturnType> createCall(String callableSql, 
                                     CallableStatementMapper<ReturnType> mapper);

If you implement this interface, the change is backwards incompatible. An implementation of Handle made against 2.2.2 will not compile against this. On the other hand, the intent of the library is not for people to implement Handle, it is to expose the libraries functionality. It is almost a header file.

So, 2.3 or 3.0?

writebacks...

Eugene Kuleshov


Rule is actually simple. If this interface is declared in "internal" packages (non-API) then it is backward compatible change. Unfortunately there is currently no way in Java to explicitly specify what packages/classes are internal. OSGi handles this using "Export-Package" attribute and jsr277/294 is trying to bring a first class support for this into Java. BTW, there are some neat API tools in Eclipse 3.4 that help to detect things like API incompatibility and API leakage, but they only work for OSGi bundles at the moment.

Craig S. Cottingham


I would say 2.3. I don't interpret "forward incompatible change" as "existing code still compiles without change", but rather "behavior of existing code does not change". You may need to add implementations for new methods (which could be as simple as "throw new UnsupportedOperationException()"), but you don't have to change any of the other code in your implementing class.

Sylvain Wallez


This problem has existed in the JDK when some methods were added to java.sql.Connection in 1.4, thus breaking all connection pool implementations. IIRC, Sun's rationale was that binary compatibility of existing code was kept (i.e. you could run pre-1.4 JDBC code on a 1.4 JVM), and that writing your own implementation of this interface was something only tool vendors or very advanced developers would do, thus not causing much harm. So I would say 2.3, with a warning that some interfaces have evolved and that _some_ code may need to be updated to compile. And clearly state in the Handle javadocs that it's an internal interface :-)

comment...

 
Name:
URL/Email: [http://... or mailto:you@wherever] (optional)
Title: (optional)
Spam Guard, translate l33t to English: (hint, it's an Australian animal, plural form)
Comments:
Save my Name and URL/Email for next time