skip to content

How do Maven version ranges work (e.g. [1.0,2.0)), and why are pinned versions usually preferred?

level: seniorimportance: should knowfreq 45%

answer

  1. [ ] inclusive, ( ) exclusive
  2. range picks HIGHEST match
  3. bare version = soft, [x] = hard
  4. ranges break reproducibility
  5. pin + BOM + deliberate upgrades

basics

~20 s

A version range like [1.0,2.0) lets Maven pick any matching version: square brackets are inclusive, parentheses exclusive. So that means >=1.0 and <2.0. Most teams instead pin an exact version (e.g. 1.4.2) for reproducible, predictable builds.

solid answer

~40 s

Maven supports mathematical-interval ranges: [ ] are inclusive bounds, ( ) exclusive. [1.0,2.0) means >=1.0 and <2.0; [1.5,] means >=1.5; (,1.0] means <=1.0; [1.2] means exactly 1.2 (a hard pin). At resolution Maven reads maven-metadata.xml and selects the highest version in the range. The catch: ranges make builds non-deterministic — a new release in the range can change your build without any pom edit, breaking reproducibility and inviting surprise breakage or supply-chain risk. They also interact awkwardly with mediation. A plain version like '1.4.2' is technically a 'soft' requirement (a recommendation that mediation can override), whereas '[1.4.2]' is a 'hard' pin. In practice teams pin exact versions, centralize them in dependencyManagement/BOMs, and upgrade deliberately via tooling like the versions plugin or Dependabot rather than relying on ranges.

code

xml · 6 lines
xml
<!-- [1.0,2.0): >=1.0 and <2.0; Maven picks the highest match -->
<dependency>
  <groupId>org.example</groupId>
  <artifactId>lib</artifactId>
  <version>[1.0,2.0)</version>
</dependency>

go deeper

for a junior

Recognizes the bracket/parenthesis inclusive-exclusive syntax.

for a middle

Knows ranges pick the highest match and reads maven-metadata.xml.

for a senior

Articulates soft-vs-hard requirements and why pinning beats ranges for reproducibility/security.

for a principal

Sets org policy: pin + BOM, ban ranges in app builds, drive upgrades via tooling and supply-chain controls.

## Range syntax (interval notation) Maven borrows math interval notation: - `[ ]` = **inclusive** bound - `( )` = **exclusive** bound - comma separates lower,upper; an empty side means unbounded. Examples: - `[1.0,2.0)` → 1.0 <= v < 2.0 - `[1.5,]` → v >= 1.5 - `(,1.0]` → v <= 1.0 - `[1.2,1.3]` → 1.2 <= v <= 1.3 - `[1.2]` → exactly 1.2 (a HARD requirement) ## Soft vs hard requirements A bare version such as `1.4.2` is a **soft** requirement: it's your recommendation, but Maven's dependency mediation (nearest-wins) can override it with another version chosen for a transitive conflict. Wrapping it as `[1.4.2]` makes it a **hard** requirement that must be satisfied exactly, or the build fails. ## How a range resolves Maven fetches `maven-metadata.xml` for the artifact, looks at the available versions, and picks the **highest** version that falls inside the range (release versions; SNAPSHOTs only if enabled). Because new releases appear over time, the same pom can resolve differently next week. ## Why pinned versions are preferred - **Reproducibility**: today's build == next year's build. A range can silently pull a newer, incompatible release. - **Stability/security**: a malicious or buggy new release in-range gets picked up automatically (supply-chain risk). - **Debuggability**: 'it worked yesterday' bugs from invisible version drift are painful. - **Mediation clarity**: ranges complicate conflict resolution. Best practice: pin exact versions, centralize them in `<dependencyManagement>`/BOMs, and bump them explicitly with tooling. ```xml <!-- avoid in app code --> <dependency> <groupId>org.example</groupId> <artifactId>lib</artifactId> <version>[1.0,2.0)</version> </dependency> <!-- prefer: explicit, reproducible --> <dependency> <groupId>org.example</groupId> <artifactId>lib</artifactId> <version>1.4.2</version> </dependency> ``` ## Where ranges still appear Some libraries (especially shared API contracts or plugins) publish ranges to express compatibility windows, and OSGi-style ecosystems use them more. But for application builds, pinning is the norm. To find/upgrade versions deliberately, use `versions:display-dependency-updates` from the versions-maven-plugin.

  • What's the difference between version '1.4.2' and '[1.4.2]'?
    '1.4.2' is a soft requirement that mediation can override; '[1.4.2]' is a hard pin that must be satisfied exactly or the build fails.
  • Within a range, which version does Maven choose?
    The highest available version that falls inside the range (per maven-metadata.xml).
  • Why do most teams avoid ranges in application builds?
    They make builds non-reproducible and add supply-chain/stability risk, since a new in-range release changes the build with no pom edit.

saying these in an interview costs you the question

  • Saying parentheses are inclusive and brackets exclusive (it's the reverse).
  • Claiming a range picks the lowest matching version (it picks the highest).
  • Recommending ranges for reproducible production builds.
  • Thinking a bare version is always honored regardless of mediation.

context