Beyond the build timestamp, what other sources of non-determinism can break a reproducible Maven build, and how do you address them?
answer
- Build-Jdk in manifest -> pin toolchain
- SNAPSHOTs -> requireReleaseDeps
- locale/timezone -> set UTC/en
- generators stamp time -> disable
- maven.build.timestamp leaks current time
basics
~20 sBesides timestamps, watch out for entry ordering, the JDK version baked into the manifest, locale/timezone effects, absolute paths, SNAPSHOT dependencies, and code generators that embed times. Pin the toolchain, avoid SNAPSHOTs, and normalize generated content.
solid answer
~40 s`project.build.outputTimestamp` handles timestamps and ordering in the archive, but a build can still vary for other reasons. The manifest records `Build-Jdk` / `Build-Jdk-Spec`, so a different JDK changes bytes — pin the toolchain (maven-toolchains-plugin) and the manifest content. **SNAPSHOT dependencies** resolve to whatever was latest at build time, so two builds can link different code — release builds must use only fixed versions (enforce with the maven-enforcer-plugin's requireReleaseDeps). Locale and timezone can affect generated text or sorting — set them explicitly. Absolute paths or usernames leaking into resources break it — avoid embedding `${user.dir}`/home paths. Code generators (protobuf, JAXB, some annotation processors) may stamp timestamps — configure them to omit timestamps. Filtering with `${maven.build.timestamp}` reintroduces the current time. Finally, plugin versions matter: only reproducible-friendly versions qualify. Verify the whole picture with `artifact:check-buildplan` and `artifact:compare`.
code
xml · 13 lines<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>no-snapshots</id>
<goals><goal>enforce</goal></goals>
<configuration>
<rules><requireReleaseDeps/></rules>
</configuration>
</execution>
</executions>
</plugin>go deeper
Knows timestamps are not the only thing that can vary.
Can name a few extra sources (JDK, SNAPSHOTs, locale) and the property to avoid (maven.build.timestamp).
Can configure enforcer/toolchains/locale and reason about generator timestamps.
Treats reproducibility as a system property, pins every nondeterministic input across the org, and enforces it with gates plus byte-comparison verification.
## Timestamps are only the start Setting `project.build.outputTimestamp` fixes the *archive* layer. A truly reproducible build needs the *inputs* and *other generators* to be deterministic too. The common offenders: ## 1. Toolchain / JDK Maven Archiver writes manifest entries like `Build-Jdk-Spec`. Building on JDK 17 vs 21 changes those bytes, and the compiler itself can emit slightly different bytecode across versions. **Fix:** pin the JDK with the `maven-toolchains-plugin` and document the exact compiler version; some teams also strip volatile manifest entries. ## 2. SNAPSHOT dependencies A `-SNAPSHOT` dependency resolves to the latest snapshot at build time, so two builds may embed different code. **Fix:** release builds must depend only on fixed releases. Enforce with: ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> <executions> <execution> <id>no-snapshots</id> <goals><goal>enforce</goal></goals> <configuration> <rules><requireReleaseDeps/></rules> </configuration> </execution> </executions> </plugin> ``` ## 3. Locale and timezone Default locale/timezone can change generated text, number/date formatting, or sort order in generated files. **Fix:** set `-Duser.language=en -Duser.country=US -Duser.timezone=UTC` (or via surefire/plugin config) so output is stable. ## 4. Absolute paths and environment leakage Resource filtering or generators may embed `${user.dir}`, home directories, or hostnames. **Fix:** never bake machine-specific paths into artifacts. ## 5. Resource filtering with volatile properties `${maven.build.timestamp}` is the *current* time — filtering it into a resource reintroduces variance. **Fix:** use `project.build.outputTimestamp` instead where you need a build time, or drop it. ## 6. Code generators / annotation processors Tools like protobuf, JAXB/XJC, or some processors stamp generated sources with the current time. **Fix:** turn off their timestamp option (e.g. protoc `--*_out` flags, XJC `-no-header`). ## 7. Non-reproducible plugin versions Only versions of packaging plugins that fixed ordering/timestamp bugs qualify. **Fix:** keep packaging plugins current; `check-buildplan` flags bad versions. ## Verifying the whole picture ```bash mvn artifact:check-buildplan # configuration lint mvn clean verify artifact:compare # rebuild + byte diff ``` Reproducibility is a *system property*: every nondeterministic input must be pinned or normalized, then proven by comparison.
- Why are SNAPSHOT dependencies a reproducibility problem?A SNAPSHOT resolves to the latest available build at resolution time, so two builds can embed different bytes of that dependency. Release builds should use only fixed versions, enforced via requireReleaseDeps.
- How can the JDK affect reproducibility even with outputTimestamp set?The manifest records the build JDK and the compiler can emit slightly different bytecode across versions; pin the toolchain so everyone uses the same JDK.
- Why is filtering ${maven.build.timestamp} into a resource dangerous?It injects the current wall-clock time into the artifact, which differs on every build and breaks byte-identity; use project.build.outputTimestamp instead.
saying these in an interview costs you the question
- Believing project.build.outputTimestamp alone is sufficient regardless of dependencies, JDK, or generators.
- Releasing with -SNAPSHOT dependencies and calling the build reproducible.
- Ignoring locale/timezone-dependent generated content.