Occasionally, the question of using Maven Version Ranges to specify dependencies arises in our projects.
The extended "syllogism" goes something like this:
- App X has Internal (our organization's) Dependency Y
- Dependency Y has an external Dependency Z (e.g.
g:a
oforg.apache.httpcomponents:httpclient
) - The provider of Dependency Z is very good about providing backward compatibility within
major.minor
versions numbers (eg. forhttpclient
,4.5.1
to4.5.2
can reliably assumed to contain only bug fixes and no API changes) - If App X wants to take advantage of an update to Dependency Z, it shouldn't require a new build of Dependency Y.
- In fact, we also have dependency Y1, Y2, Y3, etc. which all provide similar features (clients of different Web Services) and rely on the same
major.minor
version ofhttpclient
and are indifferent (aside from any relevant bug fixes) to the.incremental
version.
Therefore, Dependency Y, Y1, Y2, etc. can have their version of Dependency Z specified as a range (e.g. httpclient:[4.5., 4.6)
in order to say "any version 4.5.x
of httpclient
will suffice") allowing App X to "upgrade" to a new incremental version of Dependency Z by specifying a managed version of that dependency (i.e. internally fix the version itself to something within the provided range identified by Dependency Y)?
The alternative seems to be that Dependency Y (and related) continue to specify a specific version of Dependency Z (latest known, but could be slightly out of date) as a un-ranged <version>4.5.inc</version>
so that when Dependency Z provides a newer version, App X has the following options:
- Manage the version of Dependency Z internally (i.e. force the latest version to override Dependency Y's preferred version)
- Update Dependency Y's stated version of Dependency Z, rebuild, do the same of all Y1, Y2, Y3, etc. and then update App X's versions of Dependency Y, Y1, Y2, etc.
But .. the "I'm treating Dependency Z as flexible in practice, but unwilling to state that in the pom.xml
of Dependency Y" of #1 seems dishonest.
The "trickle-down upgrade" from #2 is the current practice and is a pain point. It seems to provide little value since there are no internal changes to code of the Dependency Y family, just the updated dependency.
Observations on the above:
- The proposal of the "syllogism" seems to violate the "Don't Use Version Ranges" school of thought. While App X has "build reproducibility", dependency Y does not necessarily... although with a limited range, it may be acceptable.
- Alternative-1 seems to be advocated by the "Don't Use Version Ranges" posts that I came across.
- Is alternative-2 (current practice) simply a necessary evil to ensure "reproducibility" in both App X and Dependency Y?
So, the key question is: is there an option or alternative which I'm overlooking which avoids feeling this tension between "trickle down upgrades" and "build reproducibility"?
Ideally, it would be something which explicitly states: "Build Dependency Y with version g:a:maj.min.inc
but leave transitivity ambiguous and accept any g:a:maj.min.?
version downstream".