Maven allows us to inherit from parent POMs to allow reuse of project or corporate standards, like:
- Forcing a given minimum maven version.
- Using a specific library version.
- Defining an exact plugin version (for maximum build reproducibility).
If the IT organization share some basic configurations across all their project, it usually represent it as a corporate POM, that is a top-level pom.xml file that is used as the parent of all the projects in their project portfolio.
For some reason, commonly this corporate POMs are versioned with a single integer number. I don’t know if it is either just a convention, or it had some origins on some specific constraint on older Maven versions which are no longer present, or if there is some logical rationale behind it.
On the other hand, I like the Semantic Versioning reasoning, and I try to comply with it in my own projects. So the simple question is: Why would we not follow the same versioning technique for corporate POMs too?
We can follow the Semantic Versioning for corporate POMs, using a Major.Minor.Patch versioning format, as any of the applications and libraries we daily use.
The key to define a semantic versioning, is that POMs are not just configuration files: They define a public API, just like any library have classes with public methods (which comprises its public API).
The difference in the interpretation is in the point of view of the person using the API: In the case of the class library, it is a developer trying to use the API to build a bigger functionality; in the case of the build tool API (pom.xml file), it’s a developer trying to use the API to generate that previously written piece of functionality. And as happens with a class library, the magnitude of the change from version to version also can be measured.
Which part of the X.Y.Z version format to change?
Let’s see some basic rules for the case when you need to change the corporate POM, being X the Major part or the version, Y the Minor part, and Z the Patch part, as defined in the Semantic Versioning page:
7. Patch version Z (x.y.Z | x > 0) MUST be incremented if only backwards compatible bug fixes are introduced. A bug fix is defined as an internal change that fixes incorrect behavior.
When applied to a corporate POM, we can interpreted it as:
Patch version Z MUST be incremented if the generated artifacts built are functionally exact the same as the generated by the previous version.
This means that a rebuild with the new POM will generate the artifacts exactly as the previous one, so the result will be a functionally equivalent version of it.
For example, if you are building a web application, where the final artifact is a .WAR file, then adding a new <developer> to the <developers> section of the corporate/parent POM will produce exactly the same web application with its original functionality untouched (except maybe some resource filter that add the new developer to some file).
8. Minor version Y (x.Y.z | x > 0) MUST be incremented if new, backwards compatible functionality is introduced to the public API. It MUST be incremented if any public API functionality is marked as deprecated. It MAY be incremented if substantial new functionality or improvements are introduced within the private code. It MAY include patch level changes. Patch version MUST be reset to 0 when minor version is incremented.
We can interpret it as:
Minor version Y MUST be incremented if the POM is modified in a way that it generates artifacts that are functionally equivalent, but essentially it produce a new different artifact. It MAY be incremented if substantial new functionality or improvements are introduced within the way the build is done, and may affect the final artifact. In both cases the build MUST work. Patch version MUST be reset to 0 when minor version is incremented.
For example, if your project use the Apache commons-lang library, defined in the section of its parent POM, and you want to update it from version 2.4 to 2.6, then you MUST change the minor version, because the generated artifact (.war file) will be different (a dependency has changed), though probably at runtime the application works the same.
It MAY be incremented if new plugin versions (commonly defined in a <pluginManagement> section) want to be used. It can happen that the plugin execution produces a different result, so is probable that the final artifact is modified too.
One thing is for sure: If you are given a new corporate/parent POM with a higher Minor number, then it is very likely the build will work out of the box, but you MUST RE-TEST the generated artifact.
9. Major version X (X.y.z | X > 0) MUST be incremented if any backwards incompatible changes are introduced to the public API. It MAY include minor and patch level changes. Patch and minor version MUST be reset to 0 when major version is incremented.
Interpretation for a POM file:
Major version X MUST be incremented if the build (probably) will break, or the generated artifacts will break. It MAY include minor and patch level changes. Patch and minor version MUST be reset to 0 when major version is incremented.
For example, you may introduce the use of a plugin which requires Maven 3+ to work, then breaking the current build (possibly done with Maven 2.2+). Another example is the update from an old version of a library to a new one, where the public API changed, and this will obviously break the build.
Sometimes it will not break the build but the generated artifacts: consider the case where you upgrade your application presentation layer code from JSTL 1.0 to JSTL 1.1 in a parent POM, the build will not break but the web application will not work properly since the JSP pages will declare <%@ taglib uri=”http://java.sun.com/jstl/core” prefix=”c” %> instead of <%@ taglib uri=”http://java.sun.com/jsp/jstl/core” prefix=”c” %>.
Of course, when in doubt, just go back to the old convention: change the Major version number and you are ready to go.
There is no need to use the current convention of “a single major version number” in your corporate/parent POMs. You just need the discipline to use the X.Y.Z format right, which have the added benefit of implicitly show us the magnitude of the required change to upgrade to a newer parent version: this information is not conveyed in the current convention.
In teams where their members are not very experimented with Maven, there may be a big initial number of parent POMs before stabilizing, so the X.Y.Z format may help greatly (specially if the organization has high people turnover).
As a rule of thumb consider this guidelines, regarding changes in your parent POMs:
- A Patch version change: A small change is required to upgrade. Artifacts should work out of the box.
- A Minor version change: A moderate size change is required to upgrade. Artifacts need to be re tested in their original runtime environment.
- A Major version change: A bigger effort is required to upgrade. Build probably break, and code may need to be upgraded too.