Libraries are developed by people who work with different philosophies to our own when it comes to breaking changes, release schedules, and so on. Build tooling almost universally has support for specifying a dependency with a fixed version value, or a range of allowed versions. Fortunately the Java ecosystem has by and large settled on always having each dependency be a single fixed version.
The argument for fixing a single dependency version is clear: we want reproducible builds of our applications, and when a dependency may change beneath our feet between builds of our application, we can have little faith that our application will continue to work without a lot of testing. It is better to remove this variable from our build process, and to therefore only move dependencies when the timing is right for us. This might be at the start of a development cycle, following a release of our Java code when we have time to thoroughly test our dependencies. This is a good time to follow the guidance to keep dependencies up to date.
In the sections below I provide sample Maven dependencies that specify a floating version, so that you will know what to look out for.
Do not do the following! Having unbounded ranges is a code smell that you should avoid!
Maven guidance
Maven has well-documented guidance on what a valid dependency version string is. Here is a Maven dependency on the Azure Storage Blob library, with a floating version specified to accept any version greater than or equal to the version 12.13.0:
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>[12.13.0,)</version>
</dependency>
As you can see in the Maven dependency version requirement specification, if we see a version string in a dependency with brackets, then we should start to sense a code smell. We would prefer to see a fixed version, or in the case of the azure-storage-blob
library, we would rather follow our use BOMs whenever available guidance and use the Azure SDK BOM.
Gradle guidance
Gradle supports Maven-style version strings, as well as prefix version ranges (e.g. 12.+
) and latest.status versions (e.g. latest.release
). The same guidance applies here - if we don't see a single fixed version value, then we should determine why what we do have is acceptable.