skip to content

What dynamic version selectors does Gradle support, and how does it choose which concrete version to resolve?

level: middleimportance: should knowfreq 40%

answer

  1. prefix / range / latest.* / +
  2. bracket inclusive, paren exclusive
  3. list → filter → status → highest
  4. version ordering: 1.10 > 1.9, rc < release
  5. componentSelection can reject candidates

basics

~10 s

Gradle supports prefix wildcards ('1.+'), Maven ranges ('[1.0,2.0)'), and the keywords 'latest.release' / 'latest.integration'. It lists candidate versions, filters by the selector and component status, then picks the highest match.

solid answer

~50 s

Gradle accepts several dynamic selectors: - **Prefix:** `'1.+'`, `'1.2.+'` — highest version with that prefix. - **Maven ranges:** `'[1.0,2.0)'`, `'(,1.5]'`, `'[2.0,)'` — bracket = inclusive, paren = exclusive. - **latest selectors:** `'latest.release'` (highest version whose status is `release`), `'latest.integration'` (highest of any status, includes snapshots). `'+'` means anything. To resolve, Gradle gathers the candidate version list from repository metadata (Maven `maven-metadata.xml` or Ivy directory listings / module metadata), filters to those matching the selector, then applies its **version ordering** (a semantic-ish comparison where, e.g., `1.10 > 1.9` and qualifiers like `rc` sort below releases). The highest surviving candidate wins. `latest.release` additionally checks the component's declared **status** (from Ivy/Gradle Module Metadata) and rejects non-release statuses. Because this depends on what's published *now*, results can drift, so dynamic selectors should be paired with dependency locking for reproducibility.

code

groovy · 5 lines
groovy
dependencies {
    implementation 'org.example:lib:1.+'
    implementation 'org.example:api:[2.0,3.0)'
    implementation 'org.example:tool:latest.release'
}

go deeper

for a junior

Recognise the common selectors ('1.+', ranges, latest.release).

for a middle

Explain the list→filter→pick-highest flow and the inclusive/exclusive bracket semantics.

for a senior

Discuss version ordering nuances, status filtering, componentSelection vetoes, and interaction with conflict resolution.

for a principal

Mandate failOnDynamicVersions plus locking org-wide; treat dynamic selectors as a dev-time convenience only.

## The selector grammar Gradle understands these dynamic forms: | Selector | Meaning | |---|---| | `1.+` / `1.2.+` | Prefix match — highest version starting with the prefix | | `[1.0,2.0)` | Range, 1.0 inclusive to 2.0 exclusive | | `(1.0,2.0)` | Both exclusive | | `[1.0,]` / `[1.0,)` | 1.0 and anything higher | | `(,2.0)` | Anything below 2.0 | | `latest.release` | Highest version with status `release` | | `latest.integration` | Highest version of any status (incl. snapshots) | | `+` | Absolute highest version (discouraged) | ## How resolution works 1. **List candidates.** Gradle reads each repository's version listing — for Maven that's `maven-metadata.xml`; for Ivy it can be a directory listing or metadata. The dynamic-versions cache (default 24h) governs how often this listing is re-fetched. 2. **Filter by selector.** Keep only versions matching the range/prefix/keyword. 3. **Filter by status (latest.* only).** `latest.release` keeps versions whose component status is `release`. Status comes from Gradle Module Metadata or Ivy descriptors; plain Maven modules default to `release` for final versions and `integration` for snapshots. 4. **Order and pick the highest.** Gradle's version comparator splits versions into numeric and non-numeric parts: numeric parts compare numerically (`1.10 > 1.9`), and special qualifiers are ranked so that `1.0 > 1.0-rc > 1.0-beta > 1.0-alpha` and dev/snapshot qualifiers sort below releases. ## Interaction with conflict resolution A resolved dynamic version still participates in Gradle's graph conflict resolution: if another path forces a higher static version, the higher one may win. You can also constrain or reject candidates with **rich versions** (`require`, `prefer`, `strictly`, `reject`) on a sibling concern, and a `componentSelection` rule can veto specific candidates: ```kotlin configurations.all { resolutionStrategy.componentSelection { all { if (candidate.version.contains("beta")) reject("no betas") } } } ``` ## Reproducibility Because step 1 depends on what is published at build time, the same source can resolve differently over time. The standard mitigation is **dependency locking** — resolve once, write `gradle.lockfile`, and have subsequent builds verify against it. `failOnDynamicVersions()` / `failOnChangingVersions()` can additionally make builds fail fast if such inputs slip in.

  • How does 'latest.release' differ from 'latest.integration'?
    `latest.release` only considers versions whose component status is `release`; `latest.integration` considers any status, so it can pick up snapshots/integration builds.
  • Does '1.+' resolve to 1.9 or 1.10 if both exist?
    1.10. Gradle's version ordering compares numeric segments numerically, so 1.10 ranks above 1.9.
  • How can you exclude pre-release candidates from a dynamic resolution?
    Add a `componentSelection` rule that rejects candidates whose version contains qualifiers like `rc`/`beta`, or use rich-version `reject`.

saying these in an interview costs you the question

  • Assuming '1.+' resolves to 1.9 over 1.10 (string compare instead of numeric).
  • Thinking brackets and parentheses are interchangeable in ranges.
  • Saying latest.release includes snapshots.

context