Checkpoint Lab — Coordinates, Dependencies, Repositories, Metadata, Transitivity, and Version Selection
Prove the complete dependency-resolution contract by capturing Maven and Gradle graphs, intentionally controlling a version conflict, isolating dependency state, and recording immutable artifact evidence.
Learning objectives
- Resolve one shared dependency family through both Maven and Gradle and explain why the initial selected versions differ.
- Capture dependency-tree/dependencyInsight evidence plus effective repository assumptions and selected artifact identity.
- Predict and intentionally change at least two graph/model outcomes, then verify the predictions independently.
- Repeat resolution using isolated dependency state and distinguish cache-assisted success from clean-room reproducibility.
- Produce a concise dependency-resolution handoff record and clean up only disposable lab state.
org.apache.commons:commons-text:1.10.0, whose published
POM declares org.apache.commons:commons-lang3:3.12.0,
plus a deliberate direct request for commons-lang3:3.9.
Re-check current versions and metadata before reusing these examples
in production.
1. Checkpoint scenario and acceptance contract
You are handing a small JVM service to a CI team. They need more than a successful build: they need to know which dependency versions were requested, which versions were selected, why each resolver made that choice, which repository policy supplied the modules, whether a clean resolver can obtain them, and which exact artifact bytes were inspected.
[ ] Maven and Gradle wrapper/tool + JDK identities recorded
[ ] same illustrative direct/transitive requests declared
[ ] Maven dependency tree captured and 3.9 selection explained
[ ] Gradle dependency graph + dependencyInsight captured and 3.12.0 selection explained
[ ] repository policy/origin assumptions captured
[ ] Prediction A made before alignment edit
[ ] both tools intentionally aligned to approved lang3 version and verified
[ ] Prediction B made before isolated-state run
[ ] fresh Maven local repository resolves expected immutable modules
[ ] fresh Gradle User Home resolves expected immutable modules
[ ] selected artifact checksum(s) recorded from disposable state
[ ] no normal user cache or production repository modified
2. Setup and preflight
Use the Maven and Gradle projects from Lesson 2, or recreate them from the complete build files below. Keep them as siblings. The required path uses Maven Central only. If internet access is unavailable, use the expected tree fixtures for the reasoning steps and label network resolution NOT EXECUTED; do not invent successful download evidence.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.academy</groupId>
<artifactId>dependency-lab-maven</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.10.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
plugins {
java
}
group = "com.example.academy"
version = "1.0.0"
repositories {
mavenCentral()
}
dependencies {
implementation("org.apache.commons:commons-text:1.10.0")
implementation("org.apache.commons:commons-lang3:3.9")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
tasks.withType<JavaCompile>().configureEach {
options.release = 17
}
java -version
cd dependency-lab/maven && ./mvnw -v
cd ../gradle && ./gradlew -version
# Record exact output; then return to dependency-lab.
3. Prediction A — resolve the initial conflict before running commands
Write your prediction first:
Maven selected commons-lang3: __________________
Reason: ________________________________________
Gradle selected commons-lang3: _________________
Reason: ________________________________________
Expected reasoning: Maven selects 3.9 because the direct request is nearer; Gradle selects 3.12.0 because its default conflict resolution selects the higher requested version. Now verify.
4. Capture resolver evidence
cd dependency-lab/maven
./mvnw -Dmaven.repo.local=.checkpoint-m2 org.apache.maven.plugins:maven-dependency-plugin:3.10.0:tree -Dincludes=org.apache.commons:commons-text,org.apache.commons:commons-lang3
./mvnw help:effective-settings -Doutput=effective-settings.xml
./mvnw help:effective-pom -Doutput=effective-pom.xml
cd dependency-lab/gradle
GRADLE_USER_HOME="$PWD/.checkpoint-gradle-home" ./gradlew dependencies --configuration runtimeClasspath
GRADLE_USER_HOME="$PWD/.checkpoint-gradle-home" ./gradlew dependencyInsight --dependency commons-lang3 --configuration runtimeClasspath
Do not compare output formatting character-for-character. Compare requested versions, selected version, selection reason, and graph paths.
5. Draw the requested graph and annotate each resolver’s selected node
flowchart TD P["Project"] --> T["commons-text 1.10.0"] P --> D["commons-lang3 3.9 direct"] T --> X["commons-lang3 3.12.0 transitive"] D --> M["Maven selected: 3.9"] X --> M D --> G["Gradle selected: 3.12.0"] X --> G
Record the selected version next to the resolver, not next to the declaration. Requested and resolved state are different facts.
6. Make the version policy intentional
The checkpoint’s approved version is 3.12.0. Change only the direct 3.9 request to 3.12.0 in both project models. Predict the result before rerunning.
Expected Maven selected version: 3.12.0
Expected Gradle selected version: 3.12.0
Expected graph difference: no cross-version conflict for commons-lang3
Unexpected result to investigate: any resolver still reports 3.9
Rerun the same tree/insight commands. This is intentionally a minimal direct alignment; later chapters teach central Maven management/BOMs and Gradle constraints/platforms for larger estates.
7. Prediction B — clean-room dependency state
Before creating fresh state, answer: should the selected versions change merely because the cache directory is empty? For fixed immutable release requests and unchanged repository policy, the expected selected graph is the same. What changes is the amount of metadata/artifact retrieval.
# Maven: separate empty local repository
cd dependency-lab/maven
./mvnw -Dmaven.repo.local=.checkpoint-m2-fresh org.apache.maven.plugins:maven-dependency-plugin:3.10.0:tree
# Gradle: separate empty User Home
cd ../gradle
GRADLE_USER_HOME="$PWD/.checkpoint-gradle-home-fresh" ./gradlew dependencies --configuration runtimeClasspath
If network resolution fails, mark the check failed/not executed and diagnose repository/network policy. Do not fall back to the warm cache and call that a clean-room pass.
8. Record artifact identity from disposable state
For Maven, the isolated repository layout is predictable enough for
a direct checkpoint. After alignment, locate
commons-lang3-3.12.0.jar under
.checkpoint-m2-fresh and hash it. For Gradle, cache
internals are deliberately not a stable public API; use the
dependency report to prove selection and, if you manually inspect
the disposable cache, treat paths as diagnostic evidence only.
cd dependency-lab/maven
find .checkpoint-m2-fresh -name 'commons-lang3-3.12.0.jar' -print
sha256sum $(find .checkpoint-m2-fresh -name 'commons-lang3-3.12.0.jar' -print | head -n 1)
# PowerShell equivalent:
# Get-FileHash (Get-ChildItem .checkpoint-m2-fresh -Recurse -Filter commons-lang3-3.12.0.jar | Select-Object -First 1).FullName -Algorithm SHA256
Checksum equality across two clean resolutions is useful byte-identity evidence for that artifact. It is not a vulnerability scan or a provenance proof.
9. Controlled failure — wrong coordinate, no trust-boundary expansion
Change commons-text to commons-texxt in
one disposable project. Predict a coordinate/repository-not-found
failure. Run the graph command in isolated state, capture the causal
message, then restore the coordinate. The success criterion is
recovery without adding repositories, disabling TLS,
bypassing checksums, or deleting shared caches.
10. Build the handoff record
Chapter 03 dependency-resolution checkpoint
Date: 2026-08-23
JDK: ____________________
Maven wrapper/core: ____________________
Gradle wrapper: ____________________
Maven dependency plugin: 3.10.0
Repository policy: Central-only lab / ____________________
Initial requests:
commons-text 1.10.0
commons-lang3 direct 3.9
commons-lang3 transitive 3.12.0 (via commons-text)
Initial Maven selected: ____________________
Initial Maven reason: ____________________
Initial Gradle selected: ____________________
Initial Gradle reason: ____________________
Approved aligned version: 3.12.0
Maven aligned graph verified: YES / NO
Gradle aligned graph verified:YES / NO
Fresh Maven repo verified: YES / NO / NOT EXECUTED
Fresh Gradle home verified: YES / NO / NOT EXECUTED
lang3 3.12.0 SHA-256: ____________________
Wrong-coordinate failure captured: YES / NO
Recovery verified: YES / NO
Shared caches deleted: MUST BE NO
11. Final verification checklist
| Invariant | Independent evidence | Pass condition |
|---|---|---|
| requested graph known | build files + commons-text published metadata | direct/transitive requests correctly documented |
| Maven selection explained | dependency:tree | initial 3.9 is attributable to nearest-definition mediation |
| Gradle selection explained | dependencies + dependencyInsight | initial 3.12.0 is attributable to conflict resolution |
| policy intentional | edited declarations + rerun graphs | both select approved 3.12.0 |
| clean resolution | fresh isolated states | same fixed resolved versions obtainable without normal caches |
| artifact identity | SHA-256 in disposable state | checksum recorded for exact selected JAR |
| failure recovery | wrong-coordinate record + restored graph | root cause preserved; no repository/trust expansion |
12. Cleanup and rollback
Restore the correct coordinate and approved aligned version first, then run one final graph verification. Delete only the named disposable state directories after confirming your working directory.
pwd
# Inspect before deletion:
find . -maxdepth 2 -type d \( -name '.checkpoint-m2*' -o -name '.checkpoint-gradle-home*' \) -print
# Delete only inside the disposable dependency-lab after review:
# rm -rf maven/.checkpoint-m2 maven/.checkpoint-m2-fresh
# rm -rf gradle/.checkpoint-gradle-home gradle/.checkpoint-gradle-home-fresh
rm -rf ~/.m2/repository, rm -rf ~/.gradle,
or shared CI cache deletion. Those are broader state stores and are
not required by this checkpoint.
Knowledge check
The Maven tree and Gradle insight report disagree on selected lang3 version before alignment. Does that prove one tool is wrong?
No. They have different default conflict semantics. The task is to explain each result and then encode the project’s intended version policy.
After both declarations are aligned to 3.12.0, a fresh Maven repository still resolves 3.9. What should you suspect?
Stale/different effective model, parent/management/profile influence, or another graph request—not the empty repository itself. Inspect the effective POM and dependency tree.
The warm build succeeds but fresh isolated resolution fails with 404. Can you mark reproducibility passed?
No. The warm cache is masking current repository availability. Preserve the discrepancy and fix the authoritative repository/artifact contract.
Why is dependencyInsight especially useful in Gradle?
It shows why a particular dependency/version was selected and the paths/selection reasons, rather than only listing the whole graph.
Why is the Maven artifact checksum recorded after the graph is aligned?
It ties byte evidence to the intentionally selected approved module/version instead of hashing an artifact from an accidental conflict result.
What production invariant does this chapter add?
The build can explain and reproduce dependency identity from declaration through metadata, conflict selection, repository/cache boundary, and selected artifact bytes.
Summary and production bridge
Chapter 03 adds dependency-resolution evidence to the build-engineering operating model: exact coordinates, metadata/transitive edges, requested versus selected versions, resolver-specific conflict reasons, repository authority, isolated cache state, and artifact identity. You can now distinguish “the build file asked for it” from “the resolver actually selected these bytes.”
Chapter 04 narrows the focus to Maven itself: installation, Maven
Wrapper behavior, settings.xml, the local repository,
and project bootstrap. The dependency model learned here will make
Maven’s environment-specific configuration much easier to reason
about.
Official references and version notes
These lessons were finalized against current primary documentation on 2026-08-23. Dependency metadata, plugin versions, repository policies, Gradle resolution behavior, and Maven/Gradle releases are version-sensitive. Verify the exact tool versions and repository policy used by your project and CI before applying production controls.
- Apache Maven — Releases History
- Maven — Introduction to the Dependency Mechanism
- Maven — Setting up Multiple Repositories
- Maven — Using Mirrors for Repositories
- Apache Maven Dependency Plugin
- Gradle 9.7.1 — Dependency Resolution
- Gradle 9.7.1 — Graph Resolution
- Gradle 9.7.1 — Viewing and Debugging Dependencies
- Gradle 9.7.1 — Declaring Versions and Ranges
- Gradle 9.7.1 — Dependency Caching
- Maven Central — commons-text 1.10.0 metadata
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.