Series MapLesson 36 / 58
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Deepen PracticeOrdered learning track

Build Reproducibility and Determinism

Reproducible build, locked versions, plugin versions, dependency versions, build timestamp, generated source, OS/locale/network dependency, local cache dependency, artifact hash, dan reproducibility review untuk Java/JAX-RS enterprise services.

22 min read4213 words
PrevNext
Lesson 3658 lesson track33–48 Deepen Practice
#build#reproducibility#determinism#maven+6 more

Build Reproducibility and Determinism

1. Core Idea

Build reproducibility berarti source input yang sama, dependency input yang sama, toolchain yang sama, dan environment build yang sama menghasilkan artifact yang sama atau setidaknya dapat dijelaskan secara deterministic.

Build determinism berarti hasil build tidak bergantung pada faktor tersembunyi seperti:

  • waktu build,
  • local machine state,
  • Maven cache yang kebetulan ada,
  • OS developer,
  • locale,
  • timezone,
  • file ordering,
  • network availability,
  • snapshot dependency yang berubah,
  • plugin version floating,
  • generated source tidak stabil,
  • environment variable tidak terdokumentasi,
  • CI runner image yang berubah diam-diam.

Mental model:

flowchart TD Source[Source Commit] --> Build[Build Process] Dependency[Dependency Graph] --> Build Plugin[Plugin Versions] --> Build Toolchain[JDK/Maven/OS Toolchain] --> Build Config[Build Config/Profile] --> Build Environment[Environment Inputs] --> Build Build --> Artifact[Artifact] Artifact --> Hash[Checksum / Digest] Artifact --> SBOM[SBOM / Provenance] Artifact --> Release[Release / Deployment]

Senior engineer harus bisa menjawab:

"Kalau kita rebuild commit ini minggu depan di CI yang bersih, apakah artifact-nya sama atau explainably equivalent?"

Jika jawabannya tidak jelas, release traceability dan rollback confidence lemah.


2. Why Reproducibility Matters

Build reproducibility penting untuk:

  • release trust,
  • incident investigation,
  • rollback confidence,
  • supply-chain security,
  • artifact provenance,
  • vulnerability response,
  • compliance/audit,
  • CI reliability,
  • developer productivity,
  • debugging local-vs-CI mismatch.

Tanpa reproducibility:

  • artifact sulit dikaitkan ke commit,
  • release tidak bisa diverifikasi,
  • bug sulit direproduksi,
  • CVE fix sulit dipastikan masuk artifact,
  • rollback bisa mengembalikan artifact yang tidak diketahui isinya,
  • local build dan CI build berbeda,
  • developer menghabiskan waktu mengejar build failure yang tidak deterministik.

Reproducibility adalah fondasi release discipline.


3. Deterministic vs Reproducible

Ada perbedaan praktis:

Deterministic Build

Input sama menghasilkan output sama.

Contoh:

  • commit sama,
  • dependency sama,
  • plugin sama,
  • JDK sama,
  • OS sama,
  • command sama,
  • output checksum sama.

Reproducible Build

Build bisa diulang oleh pihak/environment lain dengan hasil sama atau dapat diverifikasi setara.

Dalam enterprise, sering kali target realistis adalah:

  • artifact traceable,
  • input terkunci,
  • build environment terdokumentasi,
  • perbedaan output dapat dijelaskan,
  • checksum/digest disimpan,
  • SBOM/provenance tersedia.

Tidak semua build harus byte-for-byte reproducible sejak hari pertama, tetapi hidden non-determinism harus dikurangi dan diketahui.


4. Build Input Model

Build output dipengaruhi oleh banyak input.

flowchart LR Commit[Git Commit] --> Output[Build Output] Maven[Maven Version] --> Output JDK[JDK Version] --> Output Plugins[Maven Plugins] --> Output Deps[Dependencies] --> Output Profiles[Profiles] --> Output Env[Environment Variables] --> Output OS[OS / Filesystem] --> Output Time[Time / Timezone] --> Output Network[Network / Repositories] --> Output Cache[Local Cache] --> Output

Reproducibility work berarti mengidentifikasi input mana yang:

  • explicit,
  • pinned,
  • inherited,
  • implicit,
  • mutable,
  • environment-specific,
  • not documented.

Senior engineer harus membuat hidden inputs menjadi visible inputs.


5. Locked Dependency Versions

Dependency version harus terkunci.

Risiko muncul jika menggunakan:

  • version range,
  • snapshot dependency,
  • unpinned transitive override,
  • dependency yang dikontrol repository metadata berubah,
  • BOM floating version,
  • parent POM floating version.

Contoh version range anti-pattern:

<version>[1.0,2.0)</version>

Untuk enterprise release, version range membuat build sulit diprediksi.

Lebih baik:

<version>1.2.3</version>

Atau dikelola melalui BOM/parent POM:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>example</groupId>
      <artifactId>example-bom</artifactId>
      <version>1.2.3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Kuncinya bukan semua child menulis version eksplisit, tetapi version source harus jelas dan immutable untuk release.


6. Plugin Version Pinning

Maven plugin adalah executable build logic.

Jika plugin version tidak dipin, build bisa berubah saat plugin default berubah atau parent POM berubah.

Contoh anti-pattern:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
</plugin>

Lebih baik version dipin melalui pluginManagement:

<pluginManagement>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.x.y</version>
    </plugin>
  </plugins>
</pluginManagement>

Plugin yang sangat memengaruhi reproducibility:

  • compiler plugin,
  • surefire plugin,
  • failsafe plugin,
  • jar plugin,
  • war plugin,
  • shade plugin,
  • source plugin,
  • javadoc plugin,
  • dependency plugin,
  • enforcer plugin,
  • docker/image plugin,
  • OpenAPI/codegen plugin,
  • annotation processing plugin,
  • formatter/linter plugin.

Command review:

mvn help:effective-pom -Doutput=target/effective-pom.xml
mvn versions:display-plugin-updates
mvn dependency:resolve-plugins

7. Maven Wrapper and Build Tool Version

Maven version sendiri memengaruhi build.

Gunakan Maven Wrapper jika repository mengharapkan Maven version tertentu:

./mvnw -version
./mvnw clean verify

File terkait:

mvnw
mvnw.cmd
.mvn/wrapper/maven-wrapper.properties
.mvn/maven.config

Risiko tanpa wrapper:

  • developer memakai Maven berbeda,
  • CI memakai Maven berbeda,
  • plugin compatibility berbeda,
  • dependency resolution behavior berbeda,
  • output metadata berbeda,
  • troubleshooting sulit.

Internal verification:

  • apakah tim wajib memakai ./mvnw?
  • versi Maven apa yang disetujui?
  • apakah CI memakai wrapper atau Maven dari runner image?

8. JDK Version and Toolchains

JDK version memengaruhi:

  • compilation,
  • bytecode version,
  • annotation processing,
  • generated code,
  • test runtime,
  • TLS/crypto defaults,
  • locale/time behavior,
  • class library behavior,
  • performance.

Command:

java -version
javac -version
./mvnw -version

Compiler config penting:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <release>17</release>
  </configuration>
</plugin>

<release> lebih aman daripada hanya source dan target karena mengontrol API platform yang tersedia saat compile.

Maven Toolchains bisa dipakai jika build perlu JDK tertentu:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-toolchains-plugin</artifactId>
</plugin>

Internal verification:

  • build JDK berapa?
  • runtime JDK berapa?
  • apakah compiler release sesuai runtime target?
  • apakah CI dan Docker image memakai JDK/JRE yang sama family?

9. Build Timestamp Problems

Timestamp sering membuat artifact tidak deterministic.

Contoh sumber timestamp:

  • JAR manifest Build-Time,
  • generated file,
  • OpenAPI generated docs,
  • source generation,
  • packaged resource,
  • Docker image layer metadata,
  • release note generation,
  • test snapshot output.

Maven mendukung property untuk output timestamp dalam beberapa plugin:

<properties>
  <project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp>
</properties>

Atau timestamp tetap:

<properties>
  <project.build.outputTimestamp>2026-01-01T00:00:00Z</project.build.outputTimestamp>
</properties>

Namun value final harus mengikuti release policy. Jangan mengarang internal policy.

Pertanyaan review:

  • apakah JAR output mengandung timestamp build sekarang?
  • apakah timestamp diperlukan untuk debugging?
  • apakah timestamp membuat checksum berubah?
  • apakah build metadata lebih baik disimpan sebagai separate provenance daripada mengubah artifact?

10. Generated Sources

Generated source adalah sumber non-determinism yang umum.

Contoh:

  • OpenAPI client/server generation,
  • JAXB generation,
  • annotation processing,
  • MapStruct generated code,
  • QueryDSL generated code,
  • gRPC/protobuf generation,
  • build-info generation,
  • version class generation.

Risiko:

  • generated output berubah karena tool version,
  • file ordering tidak stabil,
  • timestamp masuk generated file,
  • absolute path masuk generated file,
  • OS line ending berbeda,
  • generated code tidak di-commit tetapi dibutuhkan IDE,
  • generated code di-commit tetapi tidak sync dengan source spec.

Review checklist:

  • generator version dipin?
  • generated source masuk Git atau target directory?
  • output deterministic?
  • ada timestamp/header volatile?
  • spec input versioned?
  • CI clean build menghasilkan output sama?

11. OS-Dependent Builds

Build bisa bergantung pada OS.

Perbedaan umum:

  • path separator / vs \\,
  • file permission,
  • executable bit,
  • symlink support,
  • case-sensitive filesystem,
  • line ending,
  • shell command availability,
  • GNU vs BSD tool behavior,
  • Docker Desktop behavior,
  • filesystem ordering,
  • default locale/timezone.

Contoh anti-pattern di Maven plugin/script:

cp target/*.jar /tmp/app.jar
sed -i 's/a/b/g' file
find . -name "*.java" | xargs grep Something

Di macOS, sed -i behavior berbeda dari GNU sed.

Jika script dipakai di CI Linux, local macOS, dan WSL, cross-platform behavior harus dipikirkan.

Internal verification:

  • OS developer yang umum,
  • OS CI runner,
  • apakah script hanya didukung di Linux,
  • apakah WSL didukung,
  • apakah line ending policy jelas.

12. Locale and Timezone Dependency

Build/test bisa bergantung pada locale/timezone.

Risiko:

  • date formatting berbeda,
  • sorting berbeda,
  • decimal separator berbeda,
  • text collation berbeda,
  • tests gagal hanya di CI,
  • generated docs berbeda,
  • snapshot output berbeda.

Command check:

date
locale
echo "$TZ"

CI dapat menetapkan environment:

export TZ=UTC
export LANG=C.UTF-8
export LC_ALL=C.UTF-8

Namun jangan menetapkan sembarangan tanpa memahami aplikasi. Untuk test date/time, explicit timezone biasanya lebih baik daripada bergantung global environment.

Review question:

  • apakah test bergantung default timezone?
  • apakah generated artifact bergantung locale?
  • apakah sort order stabil?
  • apakah release artifact menyimpan timestamp lokal?

13. Network-Dependent Build

Build yang butuh network bisa tidak reproducible.

Network dependency bisa muncul dari:

  • Maven dependency resolution,
  • plugin download,
  • generated client download schema,
  • integration test ke service eksternal,
  • container image pull,
  • license/scanner DB update,
  • codegen dari URL remote,
  • downloading binaries in script.

Risiko:

  • remote resource berubah,
  • service down,
  • rate limit,
  • credential expired,
  • proxy berbeda,
  • DNS issue,
  • repository mirror berbeda,
  • unreviewed artifact masuk build.

Lebih baik:

  • dependency diambil dari artifact repository internal/proxy,
  • schema/spec versioned di repo atau artifact,
  • integration test external diberi profile/gate jelas,
  • scanner DB update dipisah dari deterministic package step jika perlu,
  • container base image digest dipin untuk release.

14. Local Cache Dependency

Maven local repository di ~/.m2/repository sering menyembunyikan masalah.

Build bisa hijau di local karena dependency ada di cache, tetapi gagal di CI clean runner.

Atau sebaliknya, local gagal karena cache corrupt.

Command:

ls ~/.m2/repository
mvn -U clean verify
mvn dependency:purge-local-repository

Gunakan dengan hati-hati:

rm -rf ~/.m2/repository/group/path

Jangan biasakan rm -rf ~/.m2 sebagai default. Itu mahal dan tidak selalu menjelaskan root cause.

Pertanyaan debugging:

  • apakah dependency tersedia di repository remote?
  • apakah credential benar?
  • apakah local cache stale/corrupt?
  • apakah CI runner clean?
  • apakah snapshot update behavior berbeda?

15. Maven Profiles and Hidden Inputs

Maven profile bisa mengubah dependency, plugin, property, resource, dan test behavior.

Risiko reproducibility:

  • profile aktif otomatis by OS/JDK/env,
  • local dan CI menjalankan profile berbeda,
  • release profile mengubah artifact,
  • integration profile mengubah test set,
  • profile mengaktifkan dependency tambahan,
  • profile mengubah repository.

Command:

mvn help:active-profiles
mvn help:all-profiles
mvn help:effective-pom -Doutput=target/effective-pom.xml

Review:

  • profile apa yang aktif di local?
  • profile apa yang aktif di CI?
  • profile apa yang aktif saat release?
  • apakah activation eksplisit atau implicit?
  • apakah profile documented?

16. Environment Variables as Build Inputs

Environment variable bisa mengubah build.

Contoh:

  • JAVA_HOME,
  • MAVEN_OPTS,
  • MAVEN_CONFIG,
  • CI,
  • TZ,
  • LANG,
  • repository credentials,
  • proxy variables,
  • feature flags for tests,
  • Docker host variables,
  • cloud credentials,
  • profile activation env.

Command:

env | sort
./mvnw -version
./mvnw help:active-profiles

Security note: jangan paste full env ke PR/chat/log karena bisa mengandung secret.

Untuk evidence, redact sensitive values.


17. Build Artifact Hash

Artifact hash membantu membuktikan artifact identity.

Command:

sha256sum target/*.jar
shasum -a 256 target/*.jar

Untuk container image:

docker image inspect my-image:tag --format '{{.Id}}'
docker image inspect my-image:tag --format '{{index .RepoDigests 0}}'

Hash berguna untuk:

  • release evidence,
  • artifact comparison,
  • rollback verification,
  • supply-chain audit,
  • incident investigation.

Namun hash berubah jika artifact mengandung timestamp atau non-deterministic ordering.

Jika byte-for-byte reproducibility belum tercapai, simpan setidaknya:

  • source commit,
  • build command,
  • Maven/JDK version,
  • dependency tree,
  • SBOM,
  • artifact checksum,
  • CI run ID,
  • container digest.

18. Artifact Provenance

Provenance menjawab asal-usul artifact.

Artifact provenance minimal mencakup:

  • Git repository,
  • commit SHA,
  • branch/tag,
  • CI run ID,
  • build actor/system,
  • build timestamp,
  • command/stage,
  • JDK/Maven version,
  • dependency graph/SBOM,
  • artifact checksum,
  • container image digest,
  • deployment environment.

Mental model:

flowchart LR Commit[Commit SHA] --> CI[CI Run] CI --> Artifact[Artifact] Artifact --> Checksum[Checksum] Artifact --> SBOM[SBOM] Artifact --> Image[Container Image] Image --> Digest[Image Digest] Digest --> Deployment[Deployment] Deployment --> Runtime[Running Pod/Service]

Without provenance, release investigation becomes guesswork.


19. Reproducible JAR Concerns

JAR files can be non-deterministic because of:

  • entry timestamps,
  • file ordering,
  • manifest fields,
  • generated resources,
  • build user/path,
  • compression metadata,
  • plugin version behavior.

Maven JAR plugin and related archiver support reproducibility options in modern versions.

Review:

  • is project.build.outputTimestamp configured?
  • are plugin versions modern and pinned?
  • does manifest include volatile data?
  • are generated resources stable?
  • is file ordering deterministic?

Command comparison:

sha256sum target/app.jar
jar tf target/app.jar | head
unzip -l target/app.jar | head

A changed hash is not automatically wrong, but unexplained changed hash is a reproducibility smell.


20. Reproducible Docker Image Concerns

Even if Maven artifact is stable, Docker image may not be.

Sources of non-determinism:

  • floating base image tag,
  • apt-get update without pinned package versions,
  • downloading binary during build,
  • copying files with changing timestamps,
  • build args not recorded,
  • layer ordering,
  • Docker builder version,
  • OS package repository changes,
  • timezone/locale,
  • multi-stage build pulling latest tool image.

Bad:

FROM eclipse-temurin:17
RUN apt-get update && apt-get install -y curl

More reproducible direction:

  • pin base image by digest for release-critical builds,
  • avoid unnecessary package install,
  • use controlled internal base image,
  • copy known artifact from CI,
  • record image digest,
  • scan final image,
  • avoid downloading unpinned external files.

Internal verification: check base image policy and registry rules.


21. CI Runner Determinism

CI runner affects build.

Variables:

  • runner OS,
  • runner image version,
  • installed JDK/Maven,
  • workspace cleanliness,
  • cache restore behavior,
  • network access,
  • credentials,
  • timezone/locale,
  • CPU/memory,
  • parallelism.

Review CI config:

  • is JDK setup explicit?
  • is Maven version explicit?
  • does CI use ./mvnw?
  • are caches keyed correctly?
  • can cache hide dependency issue?
  • are environment variables documented?
  • are build artifacts uploaded?
  • is CI run linked to release artifact?

CI should be the canonical build environment for release.

Local builds are for developer feedback; release confidence should come from controlled CI.


22. Cache Strategy and Reproducibility

Cache improves speed but can reduce determinism if unmanaged.

Caches:

  • Maven local repository cache,
  • GitHub Actions cache,
  • Docker layer cache,
  • test cache,
  • generated source cache,
  • scanner DB cache.

Risks:

  • stale dependency,
  • corrupted artifact,
  • cache key too broad,
  • cache key too narrow,
  • cache hit hides missing dependency,
  • cache behavior differs by branch,
  • snapshot dependency stale.

Good cache strategy:

  • key includes OS, JDK, Maven, lock-relevant files,
  • cache is optimization, not correctness requirement,
  • clean build possible,
  • release build can be validated from clean or controlled cache,
  • cache miss does not fail build.

23. Dependency Resolution Determinism

Maven dependency resolution depends on:

  • POM,
  • parent POM,
  • BOM,
  • settings.xml,
  • mirrors,
  • repositories,
  • local cache,
  • snapshot update policy,
  • plugin repositories,
  • active profiles.

Command evidence:

./mvnw help:effective-pom -Doutput=target/effective-pom.xml
./mvnw help:effective-settings -Doutput=target/effective-settings.xml
./mvnw dependency:tree -DoutputFile=target/dependency-tree.txt

For release investigation, keep dependency tree and effective POM as artifacts if internal process allows.

Do not expose credentials from effective settings.


24. Test Determinism

Tests can be non-deterministic even when build artifact is stable.

Causes:

  • test order dependency,
  • shared static state,
  • current time,
  • random data without seed,
  • external service dependency,
  • race condition,
  • async timing,
  • port conflict,
  • filesystem temp conflict,
  • Docker/Testcontainers pull variance,
  • database state leakage,
  • timezone/locale,
  • parallel execution.

Controls:

  • isolate test data,
  • use deterministic clock,
  • seed randomness,
  • avoid fixed ports,
  • clean containers/databases,
  • separate unit and integration tests,
  • avoid sleeps; use await conditions,
  • capture logs and reports,
  • document flaky tests.

Test determinism is part of build determinism because CI signal must be trustworthy.


25. File Ordering and Archive Determinism

Some build outputs depend on file ordering.

Risks:

  • filesystem returns files in different order,
  • find output not sorted,
  • generated resource order changes,
  • archive entry order changes,
  • service loader file merge order changes.

Safer shell pattern:

find src/main/resources -type f | sort

If script builds archives manually, ensure deterministic ordering.

Maven plugins usually handle common cases better than custom scripts, but generated resources and custom packaging still need review.


26. Line Endings and File Modes

Line ending differences can affect:

  • shell scripts,
  • generated files,
  • checksum,
  • tests comparing golden files,
  • Docker build,
  • Git diff noise.

File mode differences can affect:

  • executable scripts,
  • Docker entrypoint,
  • CI scripts,
  • package artifacts.

Relevant files:

.gitattributes
.gitignore
.editorconfig
scripts/*.sh
mvnw
Dockerfile

Review:

  • are shell scripts executable?
  • are line endings normalized?
  • are generated files stable?
  • does Windows/WSL workflow preserve file modes?

27. Build Metadata

Build metadata can be useful but must be controlled.

Examples:

  • commit SHA,
  • branch,
  • tag,
  • build number,
  • CI run URL,
  • build time,
  • artifact version.

Risks:

  • metadata makes artifact non-reproducible,
  • metadata includes local path/user,
  • metadata includes secret/env,
  • metadata differs local vs CI,
  • metadata not exposed where needed.

Better approach:

  • include stable commit/tag/version,
  • avoid volatile local machine data,
  • put detailed provenance in external metadata/artifact where possible,
  • expose build info endpoint only if security policy allows,
  • ensure runtime logs include safe version identity.

28. Build Number vs Version

Do not confuse:

  • Maven version,
  • Git tag,
  • CI build number,
  • Docker image tag,
  • image digest,
  • deployment revision,
  • application runtime version.

A robust release system maps these together.

Example mapping:

Git commit: abc123
Git tag: service-1.8.4
Maven version: 1.8.4
JAR checksum: sha256:...
Docker image tag: service:1.8.4
Docker image digest: sha256:...
Deployment revision: rollout-...

If only image tag is known but digest is not, tag mutability can weaken traceability.

If only CI build number is known but commit/tag is not, debugging gets harder.


29. Release Build vs Developer Build

Developer build optimizes feedback speed.

Release build optimizes trust.

Developer build may allow:

  • skipping integration tests,
  • using local services,
  • using caches,
  • debug profile,
  • faster partial builds.

Release build should require:

  • clean controlled environment,
  • pinned tools,
  • full verification,
  • security scans,
  • SBOM/provenance,
  • artifact publishing,
  • immutable artifact,
  • traceable deployment.

Do not design release build around local convenience.

Do not force every local command to be as heavy as release build.

Separate workflows clearly.


30. Build Reproducibility in GitOps

In GitOps-oriented environments, desired state is stored in Git.

Reproducibility concerns:

  • source repo commit,
  • image digest in deployment manifest,
  • config repo commit,
  • Helm chart version,
  • Kustomize overlay,
  • environment values,
  • secret version,
  • rollout controller behavior.

A reproducible application release requires more than Maven build reproducibility.

It requires traceability across:

app source commit -> artifact -> image digest -> deployment config commit -> cluster state

Internal verification:

  • does deployment use image tag or digest?
  • how are Helm chart versions pinned?
  • how are environment values versioned?
  • how are config changes audited?

31. Build Reproducibility in Cloud Context

AWS/Azure impact:

  • container registry tag mutability,
  • artifact repository retention,
  • secret versioning,
  • build runner image,
  • IAM/RBAC permissions,
  • object storage artifact retention,
  • audit log availability,
  • regional repository differences,
  • private endpoint/network dependencies.

Questions:

  • are artifacts immutable after publish?
  • are image tags mutable?
  • is digest recorded?
  • is artifact retained long enough for audit/rollback?
  • are build logs retained?
  • are release approvals traceable?

32. Failure Modes

32.1 Same Commit, Different Artifact

Likely causes:

  • timestamp,
  • plugin version drift,
  • dependency drift,
  • generated source,
  • file order,
  • locale/timezone,
  • OS difference.

32.2 Local Build Passes, CI Fails

Likely causes:

  • local cache,
  • different JDK/Maven,
  • missing env var,
  • different profile,
  • OS difference,
  • missing credential,
  • test order/timing,
  • network access.

32.3 CI Passes, Release Fails

Likely causes:

  • release profile differs,
  • scan gate differs,
  • artifact repository permission,
  • version already exists,
  • snapshot dependency,
  • tag/version mismatch,
  • SBOM missing,
  • signing/provenance issue.

32.4 Rebuild Cannot Reproduce Old Artifact

Likely causes:

  • dependency no longer available,
  • snapshot changed,
  • plugin changed,
  • base image changed,
  • build runner changed,
  • generated resource changed,
  • repository retention issue.

33. Detection Techniques

33.1 Capture Tool Versions

java -version
javac -version
./mvnw -version
uname -a

33.2 Capture Maven Inputs

./mvnw help:active-profiles
./mvnw help:effective-pom -Doutput=target/effective-pom.xml
./mvnw dependency:tree -DoutputFile=target/dependency-tree.txt

33.3 Capture Artifact Hash

sha256sum target/*.jar

33.4 Compare Builds

mkdir -p /tmp/build-a /tmp/build-b
# build twice in clean workspaces if practical
sha256sum /tmp/build-a/*.jar /tmp/build-b/*.jar

33.5 Inspect Archive

jar tf target/*.jar | head -50
unzip -l target/*.jar | head -50

33.6 Check Profiles

./mvnw help:active-profiles

33.7 Check Environment Carefully

env | sort

Redact secret values before sharing.


34. Debugging Non-Reproducible Build

Workflow:

flowchart TD Symptom[Non-Reproducible Output] --> Compare[Compare artifacts/checksums] Compare --> Inputs[Compare inputs] Inputs --> Tools[Compare JDK/Maven/OS] Inputs --> Deps[Compare dependency tree] Inputs --> Profiles[Compare active profiles] Inputs --> Env[Compare env vars] Inputs --> Time[Check timestamp/timezone] Inputs --> Generated[Inspect generated sources] Inputs --> Cache[Check cache influence] Deps --> Fix[Pin/Align] Tools --> Fix Profiles --> Fix Env --> Fix Time --> Fix Generated --> Fix Cache --> Fix Fix --> Verify[Rebuild and compare]

Triage questions:

  • what exactly differs?
  • artifact hash or runtime behavior?
  • same workspace or clean workspace?
  • same JDK/Maven?
  • same dependency tree?
  • same active profiles?
  • same environment?
  • same timestamp behavior?
  • same generated sources?
  • same base image?

Do not start by deleting everything. First capture evidence.


35. Correctness Concerns

Non-deterministic build can become correctness issue.

Examples:

  • generated API model differs,
  • build profile changes resource filtering,
  • timezone changes test baseline,
  • dependency drift changes runtime behavior,
  • local cache hides missing dependency,
  • Docker base image changes OS library,
  • JDK changes TLS/cert behavior.

Correctness means the artifact you tested is the artifact you release.

If build output changes after tests, test evidence is weakened.


36. Productivity Concerns

Poor reproducibility causes:

  • "works on my machine",
  • CI-only failures,
  • slow onboarding,
  • random retry culture,
  • excessive cache cleaning,
  • unclear release failure,
  • distrust in pipeline,
  • large investigation cost.

Developer productivity improves when:

  • tools are versioned,
  • commands are documented,
  • CI and local are close enough,
  • release workflow is deterministic,
  • failure evidence is easy to collect,
  • cache behavior is predictable.

37. Security Concerns

Reproducibility is supply-chain security.

Risks:

  • dependency confusion,
  • artifact tampering,
  • unpinned plugin execution,
  • mutable image tag,
  • snapshot dependency in release,
  • build from untrusted repository,
  • generated source from remote URL,
  • untracked binary checked into repo,
  • secret in build metadata,
  • artifact cannot be tied to commit.

Security review should ask:

  • can we prove what source produced this artifact?
  • can we prove what dependency versions were included?
  • can we prove scan result matches deployed artifact?
  • can we prove image digest matches deployment?

38. Release Concerns

Release process depends on reproducibility.

Release checklist:

  • version final and immutable,
  • Git tag created correctly,
  • Maven artifact published once,
  • artifact checksum recorded,
  • SBOM generated,
  • image built from correct artifact,
  • image digest recorded,
  • deployment manifest references correct version/digest,
  • release note references commit/tag,
  • rollback target known,
  • build logs retained.

If artifact is rebuilt during promotion, ensure rebuild is controlled or avoid rebuild by promoting immutable artifact.

Promotion should prefer moving the same artifact forward, not creating a similar artifact repeatedly.


39. Observability and Incident Support Concerns

During incident, reproducibility helps answer:

  • what version is running?
  • which commit produced it?
  • what dependency versions are inside?
  • what config/deployment manifest was used?
  • what image digest is running?
  • can we rebuild it?
  • can we rollback to known-good artifact?

Runtime should expose safe version identity through one or more channels if approved:

  • startup log,
  • health/info endpoint,
  • metric label,
  • deployment annotation,
  • image label,
  • release dashboard.

Do not expose sensitive build metadata publicly.


40. Practical Maven Commands

Clean verify with wrapper

./mvnw clean verify

Show Maven and Java version

./mvnw -version
java -version

Active profiles

./mvnw help:active-profiles

Effective POM

./mvnw help:effective-pom -Doutput=target/effective-pom.xml

Dependency tree

./mvnw dependency:tree -DoutputFile=target/dependency-tree.txt

Plugin updates

./mvnw versions:display-plugin-updates

Dependency updates

./mvnw versions:display-dependency-updates

Artifact hash

sha256sum target/*.jar

Inspect JAR

jar tf target/*.jar | less

41. Reproducibility Evidence Bundle

For important release/build investigation, capture:

Git commit SHA
Git branch/tag
CI run ID
Build command
JDK version
Maven version
OS/runner image
Active Maven profiles
Effective POM
Dependency tree
SBOM
Artifact checksum
Container image digest
Relevant environment variables, redacted
Build logs
Test reports
Scanner results

Do not include secrets.

Evidence bundle should be attached to release artifact or accessible through CI/release system if internal policy allows.


42. PR Review Checklist

For changes affecting reproducibility, ask:

  • Does this change dependency versions?
  • Does this change plugin versions?
  • Does this change parent POM/BOM?
  • Does this add generated source?
  • Does this add build timestamp or metadata?
  • Does this add OS-specific script?
  • Does this rely on local cache?
  • Does this rely on network resource?
  • Does this change Maven profiles?
  • Does this change Docker base image?
  • Does this change CI runner/tool setup?
  • Does this change release artifact content?
  • Is build command documented?
  • Is CI using wrapper or pinned tools?
  • Is artifact checksum/provenance preserved?
  • Does rollback still use immutable artifact?
  • Are secrets excluded from build logs/metadata?

43. Internal Verification Checklist

Verify the following in the internal CSG/team context:

  • official Maven version,
  • whether Maven Wrapper is mandatory,
  • official JDK build version,
  • official runtime JDK/JRE version,
  • compiler release target,
  • parent POM and pluginManagement owner,
  • dependency/BOM version alignment policy,
  • snapshot dependency policy,
  • release build command,
  • CI runner OS/image,
  • CI cache strategy,
  • active Maven profiles in CI/release,
  • artifact repository policy,
  • artifact immutability policy,
  • SBOM/provenance requirement,
  • artifact checksum retention,
  • Docker base image policy,
  • image tag mutability policy,
  • image digest recording,
  • GitOps deployment versioning approach,
  • release evidence retention,
  • build metadata exposure policy,
  • incident process for identifying deployed version.

Do not infer these details. Verify from README, CONTRIBUTING, POM, parent POM, .mvn, workflow files, Dockerfile, deployment manifests, artifact repository, release guide, and senior/platform/SRE/security discussion.


44. Common Anti-Patterns

Avoid:

  • relying on globally installed Maven,
  • relying on unspecified JDK,
  • unpinned Maven plugin versions,
  • version ranges in release dependency,
  • snapshot dependencies in release artifact,
  • generated source with timestamps,
  • build scripts downloading latest binaries,
  • Dockerfile using floating base image for release-critical path,
  • CI cache required for correctness,
  • local-only profile for production-like build,
  • environment variables silently changing artifact,
  • release builds from dirty working tree,
  • rebuilding artifact during promotion instead of promoting immutable artifact,
  • image tags without digest traceability,
  • release note without commit/artifact mapping,
  • artifact without checksum/SBOM/provenance.

45. Senior Engineer Heuristics

Use these heuristics:

  • Make build inputs explicit.
  • Pin executable build logic.
  • Prefer wrapper over global tool assumptions.
  • Prefer immutable artifacts over rebuild-per-environment.
  • Treat CI release build as canonical.
  • Treat local build as feedback, not release proof.
  • Capture dependency tree for meaningful release/debug work.
  • Do not let cache become correctness dependency.
  • Make generated source deterministic or clearly controlled.
  • Record image digest, not only tag.
  • Keep build metadata safe and useful.
  • Investigate non-determinism with evidence, not superstition.

46. Practice Exercises

Exercise 1: Capture Build Inputs

Run:

./mvnw -version
java -version
./mvnw help:active-profiles
./mvnw help:effective-pom -Doutput=target/effective-pom.xml
./mvnw dependency:tree -DoutputFile=target/dependency-tree.txt

Summarize build inputs.

Exercise 2: Build Twice and Compare

In two clean workspaces, run:

./mvnw clean package
sha256sum target/*.jar

Compare hashes and investigate differences.

Exercise 3: Inspect Generated Sources

Find generated source/resource directories.

Check whether output contains:

  • timestamp,
  • absolute path,
  • random ID,
  • unordered files,
  • environment-specific value.

Exercise 4: Compare Local and CI

Compare:

  • JDK version,
  • Maven version,
  • active profiles,
  • command,
  • environment,
  • dependency tree,
  • test reports.

Document mismatch.


47. Summary

Build reproducibility and determinism are not academic concerns.

They directly affect:

  • release confidence,
  • rollback safety,
  • CVE response,
  • artifact audit,
  • local-vs-CI debugging,
  • incident investigation,
  • developer productivity,
  • supply-chain security.

A senior backend engineer should be able to identify build inputs, pin critical versions, detect hidden non-determinism, capture release evidence, and review changes that affect artifact trust.

The goal is simple:

The artifact we test, scan, publish, deploy, observe, and rollback should be traceable to known inputs and reproducible enough to trust.

Lesson Recap

You just completed lesson 36 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.