Friday, 22 August 2014

Semantic Versioning is Not the Solution

People often think they can introduce incompatible changes in their library API, and just increase major version number, as semantic versioning proposes, to save the library clients from problems.

It is not true.

Consider a dependency tree:
my-application
  web-server 1.1.1
    commons-logging 1.1.1
  db-client 1.1.1
    commons-logging 1.1.1
  authentication 1.1.1
    commons-logging 1.1.1
Now commons-logging changes its API incompatibly and is released as commons-logging 2.0.1. Authentication adopts commons-logging 2.0.1 while other libraries still depend on 1.1.1:
my-application
  web-server 1.1.1
    commons-logging 1.1.1
  db-client 1.1.1
    commons-logging 1.1.1
  authentication 1.1.2
    commons-logging 2.0.1
Now my-application is broken, because the dependency tree includes two versions of commons-logging which share packages, class/functions names, and thus can not be loaded simultaneously.

When you release an incompatible API this way, you essentially split the world of dependent libraries into two parts: the ones depending on the old version, and ones depending in new version. Libraries from the first part can not be used together with libraries from the second part.

A better way to introduce incompatible API is to release it as a new library, for example commons-logging2, or new-logging. Make it possible to use the new library simultaneously with the old one, e.g. it should have new package name.
Doing so will protect clients in majority of cases.

If we are releasing new library for new API, there is no need for such a thing as "major version number".

NB: in some module managers, most notably in javascript, there are no global package/class names on which different versions of a library can interfere. But in majority of programming languages that problem exists.