Build Observability and Debugging
Learn Java Source, Package, Dependency, Build, Release & Deployment Engineering - Part 024
Build observability and debugging for Maven, Gradle, CI builds, dependency resolution, plugins, classpath conflicts, and build performance incidents.
Part 024 — Build Observability and Debugging
1. Posisi Part Ini Dalam Seri
Kita sudah membahas:
- source layout;
- package dan module boundary;
- Maven/Gradle;
- dependency governance;
- supply chain;
- reproducible builds;
- quality gates;
- generated code.
Sekarang kita membahas skill yang membedakan build user dan build engineer: observability dan debugging build.
Build system bukan black box. Build system adalah execution engine yang menerima input, membentuk graph, menjalankan task/phase, menyelesaikan dependency, menghasilkan output, dan melaporkan error.
Kalau kita tidak bisa mengobservasi build, kita akan memperbaiki masalah dengan tebakan:
- hapus
.m2; - hapus
.gradle; - run
clean; - restart IDE;
- tambah exclusion random;
- pin dependency tanpa alasan;
- disable cache;
- skip test;
- rerun CI sampai hijau.
Itu bukan engineering. Itu ritual.
Part ini membangun cara berpikir sistematis untuk:
- membaca build log;
- men-debug Maven dan Gradle;
- memahami dependency resolution;
- menemukan classpath conflict;
- menemukan duplicate class;
- menganalisis plugin behavior;
- membedakan local-only dan CI-only failure;
- membuat build incident runbook;
- membangun observability untuk build platform internal.
2. Kaufman Skill Deconstruction
2.1 Target Performance Level
Setelah part ini, kita harus mampu:
- melakukan build triage tanpa trial-and-error;
- menjelaskan apa yang sedang dieksekusi Maven/Gradle;
- melihat effective POM/effective settings;
- melihat dependency tree dan conflict mediation;
- memakai Gradle
dependenciesdandependencyInsight; - membaca task graph dan up-to-date/cache status;
- membedakan failure di resolution, compile, test, package, publish, atau deploy preparation;
- membuat minimal reproduction;
- menulis build incident report;
- menentukan fix yang mengatasi root cause, bukan symptom.
2.2 Skill Map
2.3 Minimal Effective Practice
Kita tidak perlu menghafal semua flags. Kita perlu menguasai satu loop:
classify → reproduce → observe inputs → observe graph → observe execution → inspect outputs → isolate cause → fix → guardrail
3. Build as Observable System
Build bisa dimodelkan seperti sistem dataflow.
Build observability berarti kita bisa menjawab:
- Input apa yang dipakai?
- Model build apa yang dibentuk?
- Dependency versi apa yang dipilih?
- Phase/task apa yang berjalan?
- Output apa yang dihasilkan?
- Environment apa yang memengaruhi?
- Perbedaan apa antara local dan CI?
- Bagian mana yang nondeterministic?
- Evidence apa yang bisa kita simpan?
- Guardrail apa yang mencegah kejadian berulang?
4. Failure Classification
Sebelum memperbaiki build, klasifikasikan failure.
| Failure Layer | Contoh Gejala | Kemungkinan Root Cause |
|---|---|---|
| Tool bootstrap | Wrapper gagal download | network, checksum, proxy, wrapper config |
| Model construction | POM/build script invalid | syntax, plugin version, profile, convention plugin |
| Dependency resolution | artifact not found, conflict | repository, credentials, metadata, version alignment |
| Source generation | generated class missing | generator phase/task, stale output, source root |
| Compilation | symbol not found | classpath, module path, generated source, JDK mismatch |
| Annotation processing | processor not run | processor path, JDK 23 behavior, disabled proc |
| Test execution | flaky test, fork crash | parallelism, ports, timeouts, memory, classloader |
| Packaging | missing resource, duplicate entry | shade, resource filtering, generated resource |
| Publishing | 401, 409, invalid POM | credentials, staging, duplicate release, metadata |
| Cache/incremental | stale output, wrong up-to-date | undeclared inputs, cache poisoning |
| CI infrastructure | timeout, agent issue | disk, memory, network, container image, secrets |
Rule:
Jangan lompat ke solusi sebelum failure layer jelas.
5. Debugging Principles
5.1 Start From Clean Reproduction
Local dirty state adalah sumber kebohongan.
git status --short
git clean -xfd
Kemudian:
./mvnw -V -e clean verify
atau:
./gradlew --version
./gradlew clean build --stacktrace
clean bukan solusi permanen, tetapi alat diagnosis. Jika clean memperbaiki error, kemungkinan ada masalah incremental/caching/stale output.
5.2 Capture Environment
Minimal capture:
java -version
./mvnw -version
./gradlew --version
env | sort
git rev-parse HEAD
git status --short
Untuk CI:
- runner image;
- JDK distribution;
- JDK version;
- Maven/Gradle wrapper version;
- OS;
- architecture;
- memory;
- mounted cache;
- repository mirror;
- credentials source;
- proxy configuration.
5.3 Reduce the Problem
Jika root project besar:
Maven:
./mvnw -pl :order-service -am clean verify
Gradle:
./gradlew :order-service:clean :order-service:build
Kemudian kecilkan lagi:
./mvnw -pl :order-service test -Dtest=CustomerServiceTest
./gradlew :order-service:test --tests '*CustomerServiceTest'
5.4 Separate Symptom Fix From Root Fix
Symptom fix:
rm -rf ~/.m2/repository
rm -rf ~/.gradle/caches
Root fix:
- pin dependency;
- fix repository mirror;
- fix plugin version;
- declare task input/output;
- separate processor path;
- align BOM/platform;
- remove duplicate generated output;
- add CI gate.
6. Maven Observability
6.1 Maven Version and Execution Context
Start here:
./mvnw -V -version
Check:
- Maven version;
- Java version;
- Java home;
- OS;
- locale;
- encoding;
- Maven home;
- user home.
Use Wrapper to avoid “global Maven” drift.
6.2 Effective POM
Maven builds the effective model by applying:
- parent POM;
- dependency management;
- plugin management;
- profiles;
- property interpolation;
- default lifecycle bindings.
Use:
./mvnw help:effective-pom
For large output:
./mvnw help:effective-pom -Doutput=effective-pom.xml
With modern help plugin, verbose mode can show origin comments for XML elements:
./mvnw help:effective-pom -Dverbose -Doutput=effective-pom.xml
Use cases:
- why plugin version is selected;
- where dependencyManagement comes from;
- which profile is active;
- whether compiler release is configured;
- whether plugin execution is bound;
- whether generated source plugin is configured.
6.3 Effective Settings
Settings can change repository behavior.
./mvnw help:effective-settings -Doutput=effective-settings.xml
Inspect:
- mirrors;
- servers;
- proxies;
- active profiles;
- local repository path;
- repository credentials IDs.
Common CI issue:
Could not transfer artifact ...
status code: 401
Often not a dependency problem. It may be a server ID mismatch between distributionManagement/repository config and settings.xml.
6.4 Active Profiles
./mvnw help:active-profiles
Profiles can change:
- dependencies;
- plugins;
- repositories;
- properties;
- test skips;
- compiler flags;
- generated source configuration.
Rule:
Any build behavior controlled by profile must be observable and documented.
6.5 Dependency Tree
./mvnw dependency:tree
Filter:
./mvnw dependency:tree -Dincludes=com.fasterxml.jackson.core
Scope:
./mvnw dependency:tree -Dscope=runtime
Machine-readable output:
./mvnw dependency:tree -DoutputType=json -DoutputFile=dependency-tree.json
Use cases:
- why a dependency exists;
- which version won;
- whether a test dependency leaked;
- whether a processor leaked into runtime;
- whether duplicate libraries exist;
- whether a vulnerable library is direct or transitive.
6.6 Debug Output
./mvnw -X clean verify
Use -X selectively. It is verbose, but useful for:
- plugin configuration;
- repository resolution;
- lifecycle execution;
- classpath details;
- profile activation;
- exception stack traces.
Use -e for stack traces without full debug noise:
./mvnw -e clean verify
6.7 Maven Plugin Introspection
./mvnw help:describe \
-DgroupId=org.apache.maven.plugins \
-DartifactId=maven-compiler-plugin \
-Dgoal=compile \
-Ddetail
Use when:
- unsure what a plugin parameter means;
- trying to find user property names;
- migrating plugin versions;
- debugging plugin defaults.
6.8 Maven Test Debugging
Run a specific test:
./mvnw -Dtest=CustomerServiceTest test
Run a method:
./mvnw -Dtest=CustomerServiceTest#shouldApproveCustomer test
Remote debug forked tests:
./mvnw -Dmaven.surefire.debug test
Investigate fork/classloader issues:
forkCount;reuseForks;useSystemClassLoader;argLine;- memory settings;
- Surefire vs Failsafe;
- parallel execution.
6.9 Maven Reactor Debugging
Build a module and its dependencies:
./mvnw -pl :order-service -am clean verify
Resume from failure:
./mvnw -rf :order-service verify
Skip unrelated modules only for diagnosis, not as permanent CI pattern.
7. Gradle Observability
7.1 Gradle Version and Environment
./gradlew --version
Check:
- Gradle version;
- JVM version;
- Kotlin version;
- Groovy version;
- OS;
- Gradle home.
7.2 Build Lifecycle
Gradle has three high-level phases:
- initialization;
- configuration;
- execution.
Debugging question:
| Phase | Typical Failure |
|---|---|
| Initialization | settings file, included build, plugin resolution |
| Configuration | build script error, plugin config, missing property |
| Execution | task failure, compile/test/package error |
If failure occurs before any task runs, it is likely configuration/model issue.
7.3 List Tasks
./gradlew tasks
./gradlew tasks --all
Use cases:
- confirm task exists;
- find lifecycle tasks;
- find plugin-created tasks;
- verify naming convention in multi-project builds.
7.4 Dry Run
./gradlew build --dry-run
This shows task execution plan without actually running tasks.
Use cases:
- understand task graph;
- confirm
dependsOn; - confirm generator runs before compile;
- check whether a quality gate is attached to
check.
7.5 Info and Debug Logs
./gradlew build --info
Useful for:
- incremental build decisions;
- annotation processor full recompilation warnings;
- task execution details;
- cache messages;
- compiler arguments.
More verbose:
./gradlew build --debug
Use --debug carefully. It can expose secrets in logs if build logic is careless.
7.6 Stacktrace
./gradlew build --stacktrace
./gradlew build --full-stacktrace
Use when:
- plugin throws exception;
- task action fails;
- configuration cache reports error;
- dependency resolution error lacks context.
7.7 Dependency Graph
./gradlew dependencies
For specific configuration:
./gradlew :app:dependencies --configuration runtimeClasspath
Use configurations intentionally:
compileClasspath;runtimeClasspath;testCompileClasspath;testRuntimeClasspath;annotationProcessor;- custom configurations.
7.8 Dependency Insight
./gradlew :app:dependencyInsight \
--configuration runtimeClasspath \
--dependency jackson-databind
Use cases:
- why this version was selected;
- who brought this dependency;
- conflict resolution;
- constraint/platform effect;
- variant/attribute matching details.
7.9 Build Environment
./gradlew buildEnvironment
This is different from application dependencies. It shows buildscript/plugin classpath dependencies.
Use when debugging:
- plugin resolution;
- convention plugin behavior;
- classpath conflict in build logic;
- Gradle plugin version drift.
7.10 Build Scan
./gradlew build --scan
Build Scan can help analyze:
- task timeline;
- dependency resolution;
- cache hits/misses;
- test results;
- environment;
- build failures;
- performance bottlenecks.
In enterprise environments, prefer an approved Develocity/Gradle Enterprise setup rather than publishing sensitive metadata to arbitrary external endpoints.
7.11 Configuration Cache Diagnostics
./gradlew build --configuration-cache
When it fails, read the generated report.
Common causes:
- task reads project model during execution;
- task accesses environment without declaring input;
- task uses unsupported APIs;
- plugin uses mutable global state;
- custom task is not serializable-compatible.
Configuration cache failure is not “Gradle being annoying”. It often reveals build logic that was already unsafe.
8. Dependency Resolution Debugging
8.1 Maven Resolution Questions
When Maven picks a version, ask:
- Is dependency direct or transitive?
- Is version managed by parent/BOM?
- Did nearest definition win?
- Did first declaration among same-depth dependencies win?
- Is dependency optional?
- Was it excluded?
- Which repository served it?
- Is local repository stale/corrupt?
- Is mirror overriding repository?
- Is snapshot metadata changing?
Commands:
./mvnw dependency:tree -Dverbose
./mvnw dependency:tree -Dincludes=groupId:artifactId
./mvnw help:effective-pom
./mvnw help:effective-settings
8.2 Gradle Resolution Questions
When Gradle picks a version, ask:
- Which configuration?
- Was there a conflict?
- Was version selected by constraint/platform?
- Was an enforced platform used?
- Was dependency substituted?
- Was variant selection involved?
- Was capability conflict involved?
- Was dynamic version cached?
- Was module metadata used?
- Was repository order relevant?
Commands:
./gradlew :app:dependencies --configuration runtimeClasspath
./gradlew :app:dependencyInsight --configuration runtimeClasspath --dependency group:name
8.3 Classpath Conflict Pattern
Symptom:
NoSuchMethodError
NoClassDefFoundError
ClassCastException
LinkageError
Usually this is not compiler error. It is runtime classpath conflict.
Debug path:
- Identify missing/invalid class or method.
- Find which jar should contain it.
- Inspect runtime classpath.
- Compare compile vs runtime classpath.
- Check dependency tree.
- Check shading/relocation.
- Check container/app-server provided libraries.
- Add convergence/enforcer/constraints.
Commands:
jar tf some-library.jar | grep TargetClass
./mvnw dependency:tree -Dincludes=org.example
./gradlew dependencyInsight --configuration runtimeClasspath --dependency example-lib
8.4 Duplicate Class Detection
Symptoms:
- unpredictable runtime behavior;
Class path contains multiple SLF4J bindings;- service provider conflict;
- class loaded from unexpected JAR;
- JPMS split package error.
Investigation:
find ~/.m2/repository -name '*.jar' -print0 | xargs -0 -I{} sh -c 'jar tf "{}" | grep -q "com/acme/Foo.class" && echo "{}"'
Better in CI: use dedicated duplicate-class check plugin or dependency analysis tooling.
9. Plugin Behavior Debugging
9.1 Maven Plugin Debugging
Ask:
- Is plugin version pinned?
- Is execution bound to phase?
- Is execution inherited from parent?
- Is pluginManagement only defining defaults but not executing?
- Is profile activating plugin?
- Is plugin config overridden in child?
- Is goal running in the expected module?
- Is it aggregator or per-module goal?
Common mistake:
<pluginManagement>
<plugins>
<plugin>
...
</plugin>
</plugins>
</pluginManagement>
pluginManagement configures plugin defaults. It does not automatically execute plugin unless plugin is declared/bound elsewhere.
9.2 Gradle Plugin Debugging
Ask:
- Is plugin applied to root or subproject?
- Is it in
plugins {}or applied imperatively? - Does convention plugin configure tasks lazily?
- Does it use
afterEvaluate? - Does it use
subprojects/allprojects? - Does it realize tasks unnecessarily?
- Does it break configuration cache?
- Does it add dependencies to wrong configuration?
Commands:
./gradlew help --warning-mode=all
./gradlew tasks --all
./gradlew properties
./gradlew buildEnvironment
9.3 Plugin Version Drift
Build logic can drift through:
- root plugin versions;
- convention plugin dependencies;
- included build logic;
- corporate parent POM;
- Maven plugin management;
- Gradle version catalogs;
- transitive build plugin dependencies.
Guardrail:
- centralize plugin versions;
- pin versions;
- scan buildscript classpath;
- test convention plugins;
- have build-logic release notes.
10. Build Performance Debugging
10.1 First Classify Performance Issue
| Symptom | Likely Area |
|---|---|
| Build slow before tasks run | Gradle configuration, Maven model/reactor, plugin init |
| Compile slow | annotation processor, source size, JDK, incremental disabled |
| Test slow | integration tests, fork config, parallelism, containers |
| Dependency resolution slow | remote repo, snapshots, dynamic versions, metadata |
| Package slow | shading, resource processing, large artifacts |
| CI slow only | cache miss, cold agent, network, limited CPU/memory |
| Local slow only | daemon disabled, antivirus, disk, IDE interference |
10.2 Maven Performance Signals
Commands:
./mvnw -T 1C clean verify
./mvnw -X clean verify
Check:
- reactor ordering;
- plugin phase cost;
- dependency download time;
- test fork settings;
- integration test containers;
- generated source cost.
Caution:
- parallel Maven builds can expose thread-safety issues in plugins;
- do not enable
-Tblindly for release builds without validation.
10.3 Gradle Performance Signals
Commands:
./gradlew build --scan
./gradlew build --profile
./gradlew build --info
Check:
- task execution timeline;
- configuration time;
- cache hits/misses;
- up-to-date misses;
- non-cacheable tasks;
- dependency resolution time;
- annotation processor behavior.
10.4 Cache Miss Debugging
Ask:
- Which task missed cache?
- Which input changed?
- Is input declared correctly?
- Is absolute path part of input?
- Is environment variable captured?
- Is output deterministic?
- Is task cacheable?
- Is CI using the same Gradle/Maven/JDK?
- Is cache key invalidated by timestamp?
- Is remote cache trusted?
Generated code and test tasks are frequent culprits.
11. CI-Only Failure Debugging
CI-only failures are not mysterious. They are differences.
11.1 Difference Matrix
| Dimension | Local | CI |
|---|---|---|
| JDK | local installed | container/toolchain |
| OS | developer machine | Linux runner |
| File system | case-insensitive maybe | case-sensitive |
| Locale | user locale | C.UTF-8 maybe |
| Timezone | local timezone | UTC maybe |
| CPU/memory | variable | constrained |
| Network | direct | proxy/firewall |
| Cache | warm local | cold/shared |
| Credentials | user settings | secret injection |
| Tests | subset maybe | full suite |
| Profiles | local profile | CI profile |
| Parallelism | lower | higher |
| Docker | local daemon | nested/remote |
11.2 Case Sensitivity Bug
Local macOS/Windows can hide path casing problems.
Example:
import com.acme.CustomerService;
File path:
src/main/java/com/acme/customerservice.java
May behave differently across systems/tools.
11.3 Timezone/Locale Bug
Tests fail only in CI:
LocalDate.now()
NumberFormat.getCurrencyInstance()
String.toLowerCase()
Build fix:
- set test timezone/locale explicitly;
- make tests deterministic;
- don't rely on environment default.
Gradle:
tasks.withType<Test>().configureEach {
systemProperty("user.timezone", "UTC")
systemProperty("user.language", "en")
systemProperty("user.country", "US")
}
Maven Surefire:
<configuration>
<systemPropertyVariables>
<user.timezone>UTC</user.timezone>
<user.language>en</user.language>
<user.country>US</user.country>
</systemPropertyVariables>
</configuration>
11.4 Repository/Credential Bug
Symptoms:
Could not resolve artifact
401 Unauthorized
403 Forbidden
repository blocked mirror
checksum failed
Debug:
Maven:
./mvnw help:effective-settings
./mvnw -X dependency:resolve
Gradle:
./gradlew dependencies --info
./gradlew buildEnvironment --info
Check:
- secret names;
- repository URL;
- mirror;
- server ID;
- token expiry;
- repository order;
- snapshot vs release repo.
12. Output Inspection
Do not stop at “build succeeded”. Inspect artifact.
12.1 JAR Content
jar tf target/app.jar | sort | less
jar tf build/libs/app.jar | sort | less
Check:
- generated classes;
- resources;
META-INF/services;- manifest;
- duplicate entries;
- unwanted dependency classes;
- shaded packages;
- version metadata.
12.2 Manifest
unzip -p target/app.jar META-INF/MANIFEST.MF
Check:
Main-Class;Implementation-Version;- build metadata;
- classpath entries if used;
- automatic module name.
12.3 Dependency Metadata
Maven published artifact should include valid POM.
jar tf target/*.jar | grep pom
For Gradle module metadata, inspect published repository layout if applicable.
12.4 Runtime Smoke Test
For executable JAR:
java -jar target/app.jar --version
or:
java -jar build/libs/app.jar --version
For libraries, run a small consumer test.
13. Build Incident Runbook
13.1 Incident Template
# Build Incident Report
## Summary
What failed?
## Impact
Who was blocked? Which pipeline/release?
## First Failure Time
Timestamp and commit.
## Failure Layer
Resolution / compile / test / package / publish / CI infra / cache.
## Reproduction
Exact command and environment.
## Observations
Logs, dependency tree, effective POM/settings, Gradle dependencyInsight, scan link.
## Root Cause
What invariant was violated?
## Fix
What changed?
## Guardrail
What prevents recurrence?
## Follow-Up
Owner and due date.
13.2 Invariant-Based RCA
Bad RCA:
Build failed because dependency was missing.
Better RCA:
Build failed because the release repository was configured in the POM but the CI settings.xml did not contain a matching server ID after the repository manager migration. Maven could resolve public dependencies but could not resolve internal release artifacts. We added an effective-settings check to CI bootstrap and documented repository server IDs in the build platform template.
A strong RCA names:
- invariant;
- violating change;
- detection gap;
- fix;
- prevention.
14. Build Observability in Internal Platform
At team scale, ad-hoc commands are enough.
At platform scale, we need systematic build observability.
14.1 What to Capture Per Build
- commit SHA;
- branch/tag;
- build tool version;
- JDK distribution/version;
- OS/container image;
- Maven/Gradle wrapper checksum;
- dependency lockfile hash;
- effective dependency graph hash;
- generated source hash if relevant;
- artifact checksum;
- test report summary;
- quality gate result;
- SBOM location;
- provenance/attestation location;
- published artifact coordinates;
- deployment candidate ID.
14.2 Build Evidence Flow
14.3 Metrics
Build platform metrics:
| Metric | Why It Matters |
|---|---|
| Build duration p50/p95 | developer productivity |
| Test duration p95 | bottleneck detection |
| Failure rate | platform health |
| Flaky test rate | trust erosion |
| Dependency resolution time | repository health |
| Cache hit rate | performance and determinism |
| Configuration time | Gradle build logic health |
| Artifact size trend | dependency bloat |
| Number of direct dependencies | governance drift |
| Vulnerability gate failures | supply-chain risk |
| Time to repair failed build | operability |
14.4 Alerts
Alert on:
- main branch build broken beyond threshold;
- dependency repository unavailable;
- cache poisoning detected;
- artifact publish failure;
- sudden artifact size increase;
- sudden dependency graph expansion;
- build duration regression;
- quality gate bypass;
- repeated flaky test pattern.
15. Debugging Playbooks
15.1 “Class Not Found” Playbook
Symptom:
java.lang.ClassNotFoundException
java.lang.NoClassDefFoundError
Steps:
- Identify class.
- Identify expected dependency.
- Check compile classpath.
- Check runtime classpath.
- Inspect packaged artifact.
- Check scope/configuration.
- Check shading/minimization.
- Check container/app server provided libs.
- Add dependency or fix scope.
- Add test/guardrail.
Maven:
./mvnw dependency:tree -Dincludes=group:artifact
Gradle:
./gradlew dependencyInsight --configuration runtimeClasspath --dependency artifact
15.2 “No Such Method” Playbook
Symptom:
java.lang.NoSuchMethodError
Steps:
- Method existed at compile time but not runtime.
- Runtime has older/incompatible jar.
- Compare compile and runtime classpath.
- Use dependency tree/insight.
- Align versions.
- Add convergence rule/platform.
15.3 “Generated Class Missing” Playbook
Steps:
- Clean build.
- Check generator task/phase runs.
- Check output directory.
- Check source root.
- Check compile depends on generator.
- Check processor path.
- Check JDK behavior.
- Check IDE divergence.
- Add CI check.
15.4 “Dependency Version Wrong” Playbook
Maven:
- inspect effective POM;
- inspect dependency tree;
- find direct/transitive path;
- check BOM/parent;
- check nearest definition;
- fix dependencyManagement.
Gradle:
- inspect dependencyInsight;
- check platform/constraint;
- check version catalog;
- check resolution strategy;
- check dependency substitution;
- fix platform/constraints.
15.5 “Build Slow” Playbook
- Determine local vs CI.
- Measure with scan/profile/logs.
- Split configuration vs execution.
- Identify top slow tasks.
- Check cacheability.
- Check annotation processors.
- Check tests.
- Check dependency resolution.
- Apply one optimization at a time.
- Add regression metric.
15.6 “Works Locally, Fails in CI” Playbook
- Capture CI environment.
- Match JDK/toolchain.
- Run clean local build in container if possible.
- Disable local caches for reproduction.
- Compare active profiles/properties.
- Compare repository settings.
- Compare timezone/locale.
- Compare file path case.
- Compare generated code and artifact.
- Fix environment declaration.
16. Common Anti-Patterns
16.1 “Just Clean It”
clean hides stale output issues. If clean fixes it, root cause is probably undeclared input/output, bad generated source management, or cache issue.
16.2 “Just Exclude It”
Dependency exclusions can be valid, but random exclusions create hidden runtime bugs.
Before exclusion:
- identify transitive path;
- understand why it exists;
- check version alignment;
- verify runtime behavior;
- document reason.
16.3 “Just Force the Version”
Forcing versions without understanding conflict can break consumers.
Better:
- use BOM/platform;
- align logical dependency family;
- add dependency constraints;
- add convergence checks.
16.4 “Skip Tests to Unblock”
Sometimes needed during incident response, but dangerous as permanent fix.
If skipped:
- record exception;
- define expiry;
- add owner;
- track follow-up.
16.5 “Disable Cache”
Disabling cache is diagnosis, not solution.
If cache causes wrong output, fix task inputs/outputs or deterministic behavior.
16.6 “CI Is Flaky”
CI is not an explanation. It is a label. Find the variable:
- network;
- time;
- data;
- parallelism;
- state;
- resource contention;
- order dependency.
17. Command Reference
17.1 Maven
# Version and environment
./mvnw -V -version
# Effective model
./mvnw help:effective-pom
./mvnw help:effective-pom -Dverbose -Doutput=effective-pom.xml
./mvnw help:effective-settings -Doutput=effective-settings.xml
./mvnw help:active-profiles
# Dependency graph
./mvnw dependency:tree
./mvnw dependency:tree -Dincludes=groupId:artifactId
./mvnw dependency:tree -Dscope=runtime
./mvnw dependency:tree -DoutputType=json -DoutputFile=dependency-tree.json
# Debug logs
./mvnw -e clean verify
./mvnw -X clean verify
# Plugin info
./mvnw help:describe -Dplugin=compiler -Dgoal=compile -Ddetail
# Reactor
./mvnw -pl :module -am clean verify
./mvnw -rf :module verify
# Tests
./mvnw -Dtest=ClassName test
./mvnw -Dtest=ClassName#methodName test
./mvnw -Dmaven.surefire.debug test
17.2 Gradle
# Version and environment
./gradlew --version
# Task discovery
./gradlew tasks
./gradlew tasks --all
# Task graph
./gradlew build --dry-run
# Logs
./gradlew build --info
./gradlew build --debug
./gradlew build --stacktrace
./gradlew build --full-stacktrace
# Dependencies
./gradlew dependencies
./gradlew :app:dependencies --configuration runtimeClasspath
./gradlew :app:dependencyInsight --configuration runtimeClasspath --dependency jackson-databind
./gradlew buildEnvironment
# Performance
./gradlew build --scan
./gradlew build --profile
# Configuration cache
./gradlew build --configuration-cache
# Tests
./gradlew test --tests '*CustomerServiceTest'
./gradlew test --debug-jvm
18. Deliberate Practice
Drill 1 — Dependency Conflict
Create a small project where two dependencies pull different versions of the same library.
Tasks:
- inspect Maven dependency tree;
- inspect Gradle dependencyInsight;
- align versions with BOM/platform;
- document why version won;
- add guardrail.
Drill 2 — Generated Class Missing
Create generator task that is not wired before compile.
Tasks:
- reproduce failure;
- inspect task graph;
- fix
dependsOnor phase binding; - confirm clean build;
- add CI check.
Drill 3 — Runtime Classpath Bug
Compile against one version, run with another.
Tasks:
- trigger
NoSuchMethodError; - identify runtime jar;
- compare compile/runtime classpath;
- fix alignment;
- write a small runtime smoke test.
Drill 4 — CI-Only Locale Failure
Write a test that depends on default locale.
Tasks:
- make it pass locally;
- fail under different locale/timezone;
- configure test runtime deterministically;
- remove environment assumption.
Drill 5 — Build Performance Regression
Add a slow task.
Tasks:
- capture baseline;
- add slow task;
- inspect build scan/profile;
- identify regression;
- add performance note or guardrail.
19. Review Checklist
When reviewing build/debugging changes:
- Failure layer is identified.
- Reproduction command is documented.
- Environment is captured.
- Maven effective POM/settings checked if Maven issue.
- Gradle dependencyInsight/task graph checked if Gradle issue.
- Dependency fix explains why selected version is correct.
- Plugin fix pins version and scope.
- Cache fix declares inputs/outputs.
- CI-only fix removes environment assumption.
- Generated-code fix is clean-build valid.
- Artifact inspected if packaging/runtime issue.
- Guardrail added to prevent recurrence.
- Incident notes distinguish symptom from root cause.
20. Top 1% Engineer Mental Model
A beginner says:
“The build is broken.”
A normal engineer says:
“The Maven/Gradle command failed.”
A senior engineer says:
“The compile task failed because generated sources were missing.”
A top-tier engineer says:
“The build model did not express a dependency from compile to schema generation. The generated sources happened to exist locally, so developer builds passed, but CI clean builds failed. The fix is to declare generator inputs/outputs, wire the task before compile, place outputs under build/target, and add a clean-build CI gate.”
Build debugging is not about knowing every command. It is about preserving the causal chain:
input → model → graph → execution → output → artifact
Once the chain is visible, build failures stop being mysterious.
21. References
- Apache Maven Help Plugin — Effective POM: https://maven.apache.org/plugins/maven-help-plugin/effective-pom-mojo.html
- Apache Maven Help Plugin — Effective Settings: https://maven.apache.org/plugins/maven-help-plugin/effective-settings-mojo.html
- Apache Maven Dependency Plugin — dependency:tree: https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html
- Apache Maven Surefire Plugin — Debugging Tests: https://maven.apache.org/surefire/maven-surefire-plugin/examples/debugging.html
- Gradle Viewing and Debugging Dependencies: https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html
- Gradle Command-Line Interface: https://docs.gradle.org/current/userguide/command_line_interface.html
- Gradle Build Scan Basics: https://docs.gradle.org/current/userguide/build_scans.html
- Gradle Performance Guide: https://docs.gradle.org/current/userguide/performance.html
- Gradle Configuration Cache: https://docs.gradle.org/current/userguide/configuration_cache.html
You just completed lesson 24 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.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.