Monitoring and Improving the Health of your Releases
Releasing a project is one of the most important procedures you will perform, but it is often tedious and error prone. While the next chapter will go into more detail about how Maven can help automate that task and make it more reliable, this section will focus on improving the quality of the code released, and the information released with it.
An important tool in determining whether a project is ready to be released is Clirr. It detects whether the current version of a library has introduced any binary incompatibilities with the previous release. Catching these before a release can eliminate problems that are quite difficult to resolve once the code is “in the wild”. An example Clirr report is shown in Figure 6-13.
Figure 6-13: An example Clirr report
This is particularly important if you are building a library or framework that will be consumed by developers outside of your own project.
Libraries will often be substituted by newer versions to obtain new features or bug fixes, but then expected to continue working as they always have.
Because existing libraries are not recompiled every time a version is changed, there is no verification that a library is binary-compatible – incompatibility will be discovered only when there’s a failure.
But does binary compatibility apply if you are not developing a library for external consumption? While it may be of less importance, the answer here is clearly – yes. As a project grows, the interactions between the project’s own components will start behaving as if they were externally-linked. Different modules may use different versions, or a quick patch may need to be made and a new version deployed into an existing application.
This is particularly true in a Maven-based environment, where the dependency mechanism is based on the assumption of binary compatibility between versions. While methods of marking incompatibility are planned for future versions, Maven currently works best if any version of an artifact is backwards compatible, back to the first release.
By default, the Clirr report shows only errors and warnings. However, you can configure the plugin to show all informational messages, by setting the
minSeverity parameter. This gives you an overview of all the changes since the last release, even if they are binary compatible. To see this in action, add the following to the reporting section of
[...] <reporting> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>clirr-maven-plugin</artifactId> <configuration> <minSeverity>info</minSeverity> </configuration> </plugin> </plugins> </reporting> [...]
If you run
mvn clirr:clirr in
proficio-api, the report will be generated in
If you run this command, you’ll notice that Maven reports that it is using version 0.9 of
proficio-api against which to compare (and that it is downloaded if you don’t have it already):
[...] [INFO] [clirr:clirr] [INFO] Comparing to version: 0.9 [INFO] ------------------------------------------------------------- [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------- [...]
This version is determined by looking for the newest release in repository, that is before the current development version.
proficio-apiare retrieved from the repository. However, you may need to install the artifacts in your local repository yourself, which you can do by issuing the
mvn installcommand from each sub-directory:
You can change the version used with the
comparisonVersion parameter. For example, to compare the current code to the 0.8 release, run the following command:
mvn clirr:clirr -DcomparisonVersion=0.8
You’ll notice there are a more errors in the report, since this early development version had a different API, and later was redesigned to make sure that version 1.0 would be more stable in the long run.
It is best to make changes earlier in the development cycle, so that fewer people are affected. The longer poor choices remain, the harder they are to change as adoption increases. Once a version has been released that is intended to remain binary-compatible going forward, it is almost always preferable to deprecate an old API and add a new one, delegating the code, rather than removing or changing the original API and breaking binary compatibility.
In this instance, you are monitoring the
proficio-api component for binary compatibility changes only. This is the most important one to check, as it will be used as the interface into the implementation by other applications. If it is the only one that the development team will worry about breaking, then there is no point in checking the others – it will create noise that devalues the report’s content in relation to the important components.
However, if the team is prepared to do so, it is a good idea to monitor as many components as possible. Even if they are designed only for use inside the project, there is nothing in Java preventing them from being used elsewhere, and it can assist in making your own project more stable.
Like all of the quality metrics, it is important to agree up front, on the acceptable incompatibilities, to discuss and document the practices that will be used, and to check them automatically. The Clirr plugin is also capable of automatically checking for introduced incompatibilities through the
To add the check to the
proficio-api/pom.xml file, add the following to the build section:
[...] <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>clirr-maven-plugin</artifactId> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> [...] </plugins> </build> [...]
If you now run
mvn verify, you will see that the build fails due to the binary incompatibility introduced between the 0.9 preview release and the final 1.0 version. Since this was an acceptable incompatibility due to the preview nature of the 0.9 release, you can choose to exclude that from the report by adding the following configuration to the plugin:
[...] <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>clirr-maven-plugin</artifactId> <configuration> <excludes> <exclude>**/Proficio</exclude> </excludes> </configuration> [...] </plugin>
This will prevent failures in the Proficio class from breaking the build in the future. Note that in this instance, it is listed only in the build configuration, so the report still lists the incompatibility. This allows the results to be collected over time to form documentation about known incompatibilities for applications using the library.
A limitation of this feature is that it will eliminate a class entirely, not just the one acceptable failure. Hopefully a future version of Clirr will allow acceptable incompatibilities to be documented in the source code, and ignored in the same way that PMD does.
With this simple setup, you can create a very useful mechanism for identifying potential release disasters much earlier in the development process, and then act accordingly. While the topic of designing a strong public API and maintaining binary compatibility is beyond the scope of this book, the following articles and books can be recommended:
- Evolving Java-based APIs contains a description of the problem of maintaining binary compatibility, as well as strategies for evolving an API without breaking it.
- Effective Java describes a number of practical rules that are generally helpful to writing code in Java, and particularly so if you are designing a public API.
A similar tool to Clirr that can be used for analyzing changes between releases is JDiff. Built as a Javadoc doclet, it takes a very different approach, taking two source trees and comparing the differences in method signatures and Javadoc annotations. This can be useful in getting a greater level of detail than Clirr on specific class changes. However, it will not pinpoint potential problems for you, and so is most useful for browsing. It has a functional Maven 2 plugin, which is available here.