Monitoring and Improving the Health of your Dependencies
Many people use Maven primarily as a dependency manager. While this is only one of Maven’s features, used well it is a significant time saver.
Maven 2.0 introduced transitive dependencies, where the dependencies of dependencies are included in a build, and a number of other features such as scoping and version selection. This brought much more power to Maven’s dependency mechanism, but does introduce a drawback: poor dependency maintenance or poor scope and version selection affects not only your own project, but any projects that depend on your project. Left unchecked, the full graph of a project’s dependencies can quickly balloon in size and start to introduce conflicts.
The first step to effectively maintaining your dependencies is to review the standard report included with the Maven site. If you haven’t done so already, run
mvn site in the
proficio-core directory, and browse to the file generated in
target/site/dependencies.html. The result is shown in Figure 6-11.
Figure 6-11: An example dependency report
This report shows detailed information about your direct dependencies, but more importantly in the second section it will list all of the transitive dependencies included through those dependencies. It’s here that you might see something that you didn’t expect – an extra dependency, an incorrect version, or an incorrect scope – and choose to investigate its inclusion.
Currently, this requires running your build with debug turned on, such as
mvn -X package. This will output the dependency tree as it is calculated, using indentation to indicate which dependencies introduce other dependencies, as well as comments about what versions and scopes are selected, and why. For example, here is the resolution process of the dependencies of
proficio-core (some fields have been omitted for brevity):
proficio-core:1.0-SNAPSHOT junit:3.8.1 (selected for test) plexus-container-default:1.0-alpha-9 (selected for compile) plexus-utils:1.0.4 (selected for compile) classworlds:1.1-alpha-2 (selected for compile) junit:3.8.1 (not setting scope to: compile; local scope test wins) proficio-api:1.0-SNAPSHOT (selected for compile) proficio-model:1.0-SNAPSHOT (selected for compile)
Here you can see that, for example,
proficio-model is introduced by
proficio-api, and that plexus-container-default attempts to introduce
junit as a compile dependency, but that it is overridden by the test scoped dependency in
This can be quite difficult to read, so at the time of this writing there are two features that are aimed at helping in this area:
- The Repository Manager (Archiva) will allow you to navigate the dependency tree through the metadata stored in the Ibiblio repository.
- A dependency graphing plugin that will render a graphical representation of the information.
Another report that is available is the “Dependency Convergence Report”. This report is also a standard report, but appears in a multi-module build only. To see the report for the Proficio project, run
mvn site from the base
proficio directory. The file
target/site/dependency-convergence.html will be created, and is shown in Figure 6-12.
The report shows all of the dependencies included in all of the modules within the project. It also includes some statistics and reports on two important factors:
- Whether the versions of dependencies used for each module is in alignment. This helps ensure your build is consistent and reduces the probability of introducing an accidental incompatibility.
- Whether there are outstanding SNAPSHOT dependencies in the build, which indicates dependencies that are in development, and must be updated before the project can be released.
Figure 6-12: The dependency convergence report
These reports are passive – there are no associated checks for them. However, they can provide basic help in identifying the state of your dependencies once you know what to find. To improve your project’s health and the ability to reuse it as a dependency itself, try the following recommendations for your dependencies:
- Look for dependencies in your project that are no longer used
- Check that the scope of your dependencies are set correctly (to test if only used for unit tests, or runtime if it is needed to bundle with or run the application but not for compiling your source code).
- Use a range of supported dependency versions, declaring the absolute minimum supported as the lower boundary, rather than using the latest available. You can control what version is actually used by declaring the dependency version in a project that packages or runs the application.
- Add exclusions to dependencies to remove poorly defined dependencies from the tree. This is particularly the case for dependencies that are optional and unused by your project.
Given the importance of this task, more tools are needed in Maven. Two that are in progress were listed above, but there are plans for more:
- A class analysis plugin that helps identify dependencies that are unused in your current project
- Improved dependency management features including different mechanisms for selecting versions that will allow you to deal with conflicting versions, specification dependencies that let you depend on an API and manage the implementation at runtime, and more.