skip to content

How does Maven parse and order version strings, and why does that matter for ranges and conflict resolution?

level: principalimportance: nice to knowfreq 25%

answer

  1. tokenize on . and - and digit/letter shifts
  2. numeric compares numerically
  3. alpha<beta<milestone<rc<snapshot<release<sp
  4. unknown qualifier sorts after release
  5. ranges use [ ] ( ); nearest-wins still mediates

basics

~20 s

Maven splits versions into numeric and qualifier tokens separated by dots/dashes and compares them token by token. Known qualifiers like alpha < beta < milestone < rc < snapshot < (release) < sp order specially; unknown qualifiers sort alphabetically after the release.

solid answer

~40 s

Maven's version comparison (in the ComparableVersion class) tokenizes a version into a sequence of numeric and string parts, separated by `.` and `-`. Numeric tokens compare numerically; string tokens are mapped to a known qualifier order: alpha/a < beta/b < milestone/m < rc/cr < snapshot < "" (the final release) < sp. A bare release (no qualifier) outranks its pre-release qualifiers, so 1.0 > 1.0-rc1 > 1.0-beta. Trailing zero segments are normalized (1.0 == 1.0.0). This ordering drives version RANGES like [1.0,2.0) and, historically, latest/newest selection — though note nearest-wins (not highest-version) is the default for transitive conflict mediation. Understanding the algorithm matters when you hit surprising range resolution or when a custom qualifier sorts unexpectedly (unknown qualifiers sort case-insensitively and AFTER known ones, i.e. after release).

code

xml · 7 lines
xml
<!-- Version range: at least 1.2, less than 2.0 -->
<dependency>
  <groupId>com.example</groupId>
  <artifactId>lib</artifactId>
  <version>[1.2,2.0)</version>
</dependency>
<!-- 1.0 > 1.0-rc1 > 1.0-beta1 ; 1.0-sp1 > 1.0 -->

go deeper

for a junior

Know numbers compare numerically and SNAPSHOT is below the final release.

for a middle

Recall the qualifier precedence list and basic range notation.

for a senior

Explain how ordering interacts (and does not) with nearest-wins mediation and why ranges hurt reproducibility.

for a principal

Govern versioning conventions (semver-aligned qualifiers, banning ranges), and teach the comparison algorithm to avoid resolution surprises.

## Why version parsing matters Maven sometimes must decide which of two versions is "newer" — for **version ranges** (e.g. `[1.2,2.0)`), for plugin `LATEST`/`RELEASE` (deprecated for deps), and for some tooling. It does this with a defined algorithm (the `ComparableVersion` class), and the rules surprise people. ## Tokenization Maven splits the version string into a list of tokens at each `.` and `-`, and also at every transition between digits and letters. Each token is either **numeric** or a **qualifier** (string). - Numeric tokens compare as integers: `9 < 10`. - Trailing "null" tokens (zeros / empty) are stripped so `1.0 == 1 == 1.0.0`. ## Qualifier ordering String qualifiers map to a fixed precedence (case-insensitive). From lowest to highest: ``` alpha (= a) < beta (= b) < milestone (= m) < rc (= cr) < snapshot < "" (release) < sp ``` Key consequences: - The **final release** (empty qualifier) is **greater** than its pre-releases: `1.0 > 1.0-SNAPSHOT > 1.0-rc1 > 1.0-beta1 > 1.0-alpha1`. - `sp` (service pack) outranks the plain release: `1.0-sp1 > 1.0`. - **Unknown** qualifiers (anything not in the list) sort **lexicographically and after** the release tier — so `1.0-foo > 1.0` and they compare alphabetically among themselves. ## Example ordering (ascending) ``` 1.0-alpha1 1.0-beta1 1.0-milestone1 1.0-rc1 1.0-SNAPSHOT 1.0 1.0-sp1 1.0.1 1.1 2.0 ``` ## Version ranges Ranges use interval notation and rely on this ordering: ```xml <dependency> <groupId>com.example</groupId> <artifactId>lib</artifactId> <version>[1.2,2.0)</version> <!-- >=1.2 and <2.0 --> </dependency> ``` - `[ ]` inclusive, `( )` exclusive. - `[1.0]` pins exactly 1.0. - Ranges are generally discouraged for production because they make builds non-reproducible. ## Relationship to conflict mediation A common trap: Maven's default transitive conflict resolution is **nearest-wins** (shortest path in the dependency tree), NOT "highest version." Version comparison governs ranges and explicit selection, but it does not override nearest-wins mediation. Knowing both rules avoids mis-diagnosing which version got picked.

  • Is 1.0 greater or less than 1.0-SNAPSHOT?
    Greater. The empty/release qualifier outranks SNAPSHOT and all pre-release qualifiers, so 1.0 > 1.0-SNAPSHOT.
  • Does Maven pick the highest version among transitive conflicts by default?
    No. The default is nearest-wins (shortest path in the dependency tree); version ordering governs ranges and explicit selection, not transitive mediation.
  • Where does an unknown qualifier like '1.0-Final' sort?
    After the plain release tier and alphabetically among unknowns, so it can sort surprisingly high; prefer recognized qualifiers.

saying these in an interview costs you the question

  • Saying 1.0-SNAPSHOT > 1.0
  • Claiming Maven always resolves transitive conflicts to the highest version (it's nearest-wins)
  • Assuming version comparison is plain string/lexical comparison
  • Recommending open version ranges for production builds

context