skip to content

Explain Maven's CI-friendly versions using ${revision}, ${sha1}, and ${changelist}. Why are they used and what pitfall do they introduce?

level: seniorimportance: should knowfreq 45%

answer

  1. only ${revision}/${sha1}/${changelist} allowed in <version>
  2. set once via property or -Drevision
  3. deployed POM keeps literal ${revision}
  4. flatten-maven-plugin resolveCiFriendliesOnly
  5. changelist often -SNAPSHOT

basics

~20 s

CI-friendly versions put a single version in one property (${revision}, plus optional ${sha1}/${changelist}) so you set it once, e.g. via -Drevision=1.2.3, instead of editing every module. The pitfall: the published POM still contains the literal ${revision}, so you must use flatten-maven-plugin to resolve it.

solid answer

~40 s

Maven 3.5+ allows the project `<version>` to be exactly `${revision}`, `${revision}${sha1}`, or `${revision}${changelist}` — three reserved properties Maven resolves even though they appear in the version. You define `<revision>` in one place (parent properties or via `-Drevision=1.2.3` / `.mvn/maven.config`), and all modules inherit it, so a CI pipeline can stamp the version from the build number or git describe without touching POMs. The catch: by default the **deployed/installed POM is not interpolated**, so consumers would see a literal `${revision}` and fail to resolve. You must add the `flatten-maven-plugin` to produce a `.flattened-pom.xml` with the real version (and inlined parent/properties) that gets installed/deployed instead. Without flattening, CI-friendly versions break downstream consumers.

code

xml · 7 lines
xml
<version>${revision}${changelist}</version>
<properties>
  <revision>1.2.3</revision>
  <changelist>-SNAPSHOT</changelist>
  <sha1/>
</properties>
<!-- Build a release in CI:  mvn -Drevision=1.2.3 -Dchangelist= deploy -->

go deeper

for a junior

Aware that the version can come from a single property/CI flag.

for a middle

Can wire ${revision}/${changelist} and set them via -D or maven.config.

for a senior

Knows the un-interpolated-POM pitfall and fixes it with flatten-maven-plugin; chooses CI-friendly vs release-plugin deliberately.

for a principal

Designs the org's versioning-in-CI strategy (git-describe stamping, tag ownership, flatten policy) and its reproducibility implications.

## The problem they solve In a large multi-module build, the version appears in the parent and every child's `<parent>` block. Bumping it means editing many files. CI-friendly versions let you keep the version in **one property** that CI can override per build. ## The three reserved placeholders Maven only allows these specific properties inside `<version>`: - `${revision}` — the main version, e.g. `1.2.3`. - `${sha1}` — an optional build/commit qualifier. - `${changelist}` — an optional suffix, often `-SNAPSHOT`. ```xml <project> <groupId>com.acme</groupId> <artifactId>app</artifactId> <version>${revision}${changelist}</version> <properties> <revision>1.2.3</revision> <changelist>-SNAPSHOT</changelist> <sha1/> </properties> </project> ``` ## Setting the value in CI - On the command line: `mvn -Drevision=1.2.3 -Dchangelist= deploy` (empty changelist = a release). - Or persist defaults in `.mvn/maven.config`: ``` -Drevision=1.2.3 -Dchangelist=-SNAPSHOT ``` ## The critical pitfall: un-interpolated published POM Maven resolves these placeholders for the **build**, but historically the **installed/deployed pom.xml keeps the literal `${revision}`**. A downstream consumer reading that POM cannot resolve the property and the dependency fails. ### Fix: flatten-maven-plugin It generates `.flattened-pom.xml` with the version (and parent/properties) fully resolved, and configures install/deploy to publish **that** file: ```xml <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>flatten-maven-plugin</artifactId> <version>1.6.0</version> <configuration> <updatePomFile>true</updatePomFile> <flattenMode>resolveCiFriendliesOnly</flattenMode> </configuration> <executions> <execution><id>flatten</id><phase>process-resources</phase><goals><goal>flatten</goal></goals></execution> <execution><id>flatten.clean</id><phase>clean</phase><goals><goal>clean</goal></goals></execution> </executions> </plugin> ``` `flattenMode=resolveCiFriendliesOnly` resolves just the version placeholders while leaving the rest of the POM intact. ## Trade-offs vs maven-release-plugin - CI-friendly + flatten: no SCM commits/tags during the build; the pipeline owns versioning and tagging. Cleaner for GitOps/CI. - release-plugin: SCM-driven ceremony with backups/resume, but heavier and chatty with commits.

  • Why can't you just put any property in <version>?
    Maven only special-cases the three reserved names (revision, sha1, changelist) for early resolution; arbitrary properties aren't resolved in the version position consistently and break tooling.
  • What exactly breaks if you skip flatten-maven-plugin?
    The installed/deployed pom.xml keeps the literal ${revision}, so downstream consumers can't resolve the version and dependency resolution fails.
  • How would you produce a release vs snapshot with this setup?
    Leave changelist=-SNAPSHOT for snapshots; pass -Dchangelist= (empty) for a release, or set revision directly to the final version.

saying these in an interview costs you the question

  • Claiming any arbitrary property works in <version>
  • Forgetting that the deployed POM is not interpolated without flatten
  • Saying it requires the release plugin (it's an alternative)
  • Thinking ${revision} is resolved in the published POM automatically

context