Chapter 13Lesson 05~210 minutes

Checkpoint Lab — Maven Performance, Parallel Builds, Daemon Options, Reproducible Builds, and Troubleshooting

Profile a multi-module Maven build, make one evidence-backed performance improvement, and prove reproducible output across isolated workspaces and local repositories.

Checkpoint LabBenchmarkingParallel BuildClean RoomVerification

This checkpoint accepts a performance change only when its timing evidence is controlled and its output identity survives an isolated rebuild. The lab uses a harmless 600 ms Ant sleep in each of two independent modules to make reactor overlap visible even on a small machine; the plugin goal is currently documented as thread-safe.

Current baseline — verified 2026-08-24. Mandatory labs use Apache Maven 3.9.16 through Maven Wrapper 3.3.4, JDK 21 as the build runtime, and Java 17 as the compiler release target. Maven Daemon 1.0.6 is an optional separate tool; Maven Daemon 2.0.0-rc-3 and Maven 4.0.0-rc-6 are preview releases and are not the production baseline. The checkpoint additionally pins Maven AntRun Plugin 3.2.0 for a synthetic, thread-safe timing fixture and Maven Artifact Plugin 3.6.1 for reproducibility-plan inspection. The sleep is pedagogy, not a production optimization technique.

Learning objectives

  • Capture a baseline reactor graph, versions, cache state, and serial timing.
  • Predict which modules can overlap and which cannot before enabling parallelism.
  • Make one measured performance improvement without changing required build scope.
  • Build the same source in isolated directories/repositories and compare checksums.
  • Diagnose any checksum or execution-plan difference before accepting the optimization.
  • Produce a verification checklist that a CI reviewer could audit.

1. Checkpoint acceptance contract

The checkpoint passes only if you can show all of the following: exact Maven/JDK identity; the common → {alpha,beta} → app graph; cold and warm baseline context; serial and parallel timings measured under the same warm repository state; no unresolved thread-safety warning; successful required lifecycle; matching expected artifact set; and matching checksums across independent workspaces after reproducibility controls are applied.

“Parallel was faster on my laptop” is not sufficient.

2. Setup and preflight

set -euo pipefail
LAB="$PWD/../maven-perf-checkpoint-a"
mkdir -p "$LAB"/{evidence,.lab/repo}
cp mvnw mvnw.cmd "$LAB/"
cp -R .mvn "$LAB/"
cd "$LAB"
./mvnw -v | tee evidence/maven-version.txt
java -version 2> evidence/java-version.txt
printf '%s
'   'Prediction 1: common runs before alpha/beta; app waits for both.'   'Prediction 2: -T 2 can overlap the 600 ms alpha/beta fixture.'   'Prediction 3: serial and parallel artifacts remain byte-identical.'   'Prediction 4: a second workspace/repository produces the same JAR checksums.'   > evidence/predictions.txt

3. Create the graph and the measurable, thread-safe fixture

<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>dev.academy</groupId><artifactId>perf-checkpoint</artifactId><version>1.0.0</version><packaging>pom</packaging>
  <modules><module>common</module><module>alpha</module><module>beta</module><module>app</module></modules>
  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.outputTimestamp>2026-08-24T00:00:00Z</project.build.outputTimestamp>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencyManagement><dependencies>
    <dependency><groupId>dev.academy</groupId><artifactId>common</artifactId><version>${project.version}</version></dependency>
    <dependency><groupId>dev.academy</groupId><artifactId>alpha</artifactId><version>${project.version}</version></dependency>
    <dependency><groupId>dev.academy</groupId><artifactId>beta</artifactId><version>${project.version}</version></dependency>
  </dependencies></dependencyManagement>
  <build>
    <pluginManagement><plugins>
      <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-clean-plugin</artifactId><version>3.5.0</version></plugin>
      <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.5.0</version></plugin>
      <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.15.0</version></plugin>
      <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.5.6</version></plugin>
      <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.5.1</version></plugin>
      <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-antrun-plugin</artifactId><version>3.2.0</version></plugin>
    </plugins></pluginManagement>
  </build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <parent><groupId>dev.academy</groupId><artifactId>perf-checkpoint</artifactId><version>1.0.0</version><relativePath>../pom.xml</relativePath></parent>
  <artifactId>alpha</artifactId>
  <dependencies><dependency><groupId>dev.academy</groupId><artifactId>common</artifactId></dependency></dependencies>
  <build><plugins><plugin>
    <groupId>org.apache.maven.plugins</groupId><artifactId>maven-antrun-plugin</artifactId>
    <executions><execution><id>measurable-work</id><phase>generate-resources</phase><goals><goal>run</goal></goals>
      <configuration><target><sleep milliseconds="600"/></target></configuration>
    </execution></executions>
  </plugin></plugins></build>
</project>

Create beta/pom.xml from the alpha POM by changing only <artifactId>alpha</artifactId> to beta. Create common and app POMs using the same parent/dependency pattern from Lesson 2. The AntRun run goal in version 3.2.0 is documented as thread-safe; the <sleep> task simply creates enough independent wall time to observe overlap.

Do not copy this sleep into production builds. It is a deterministic teaching fixture that makes the scheduler visible. Real performance work should optimize actual compilation, tests, packaging, resolution, or project graph structure.

4. Add deterministic sources and draw three separate views

Reuse the four Java classes from Lesson 2. Before running Maven, draw these views in evidence/graphs.txt:

  • Aggregation: root lists common, alpha, beta, app.
  • Dependency graph: common → alpha, common → beta, alpha → app, beta → app.
  • Parallel opportunity: alpha and beta can overlap after common; app is the join point.

Do not draw pluginManagement as a reactor ordering edge; management alone does not instantiate a project relationship.

5. Inspect before timing

./mvnw help:effective-pom -Doutput=evidence/effective-pom.xml
./mvnw org.apache.maven.plugins:maven-artifact-plugin:3.6.1:check-buildplan   -Dcheck.buildplan.tasks=verify | tee evidence/buildplan.txt
./mvnw -DskipTests dependency:tree | tee evidence/dependency-tree.txt

Confirm AntRun 3.2.0 is active only in alpha and beta and the other pinned plugin versions are inherited. Preserve any warning rather than filtering it away.

6. Capture cold and warm serial baselines

REPO="$PWD/.lab/repo"
TIMEFORMAT='elapsed=%3R s'

time ./mvnw -Dmaven.repo.local="$REPO" -T 1 clean verify   | tee evidence/serial-cold.log

time ./mvnw -Dmaven.repo.local="$REPO" -T 1 clean verify   | tee evidence/serial-warm-1.log

time ./mvnw -Dmaven.repo.local="$REPO" -T 1 clean verify   | tee evidence/serial-warm-2.log

Use the warm serial runs as the comparator for parallelism because their repository state matches the subsequent parallel run. Keep the cold run as separate evidence of resolver/bootstrap cost.

7. Apply one bounded performance change: -T 2

time ./mvnw -Dmaven.repo.local="$REPO" -T 2 clean verify   | tee evidence/parallel-warm-1.log

time ./mvnw -Dmaven.repo.local="$REPO" -T 2 clean verify   | tee evidence/parallel-warm-2.log

Expected structural observation: the two 600 ms branch fixtures can overlap, so -T 2 should usually reduce the portion of the critical path occupied by alpha and beta. Do not invent a required percentage; record the actual machine result. If parallelism is not faster, that is valid evidence—your “improvement” must then be rejected or replaced by another measured change.

8. Prove the optimization did not skip work

grep -E 'common|alpha|beta|app|Reactor Summary|BUILD SUCCESS|WARNING'   evidence/parallel-warm-1.log | tee evidence/parallel-summary.txt

sha256sum common/target/common-1.0.0.jar           alpha/target/alpha-1.0.0.jar           beta/target/beta-1.0.0.jar           app/target/app-1.0.0.jar   | tee evidence/workspace-a.sha256

The reactor summary must still contain all four projects. A faster command that uses a smaller -pl scope is not equivalent unless the checkpoint explicitly changed its required output contract.

9. Rebuild in workspace B with a separate repository

cd ..
cp -R maven-perf-checkpoint-a maven-perf-checkpoint-b
find maven-perf-checkpoint-b -type d -name target -prune -exec rm -rf {} +
rm -rf maven-perf-checkpoint-b/evidence maven-perf-checkpoint-b/.lab
mkdir -p maven-perf-checkpoint-b/{evidence,.lab/repo}
cd maven-perf-checkpoint-b

./mvnw -Dmaven.repo.local="$PWD/.lab/repo" -T 1 clean verify   | tee evidence/workspace-b.log
sha256sum common/target/common-1.0.0.jar           alpha/target/alpha-1.0.0.jar           beta/target/beta-1.0.0.jar           app/target/app-1.0.0.jar   | tee evidence/workspace-b.sha256

Compare the digest values by artifact name, not by absolute path. If any differ, stop the performance sign-off and investigate dynamic resources, plugin versions, JDK identity, generated ordering, line endings, or other undeclared inputs.

10. Add Maven reproducibility evidence

./mvnw -Dmaven.repo.local="$PWD/.lab/repo"   org.apache.maven.plugins:maven-artifact-plugin:3.6.1:check-buildplan   -Dcheck.buildplan.tasks=verify -Ddiagnose=true   | tee evidence/repro-diagnose.txt

This check knows about reproducibility issues in the execution plan and can diagnose the effective output timestamp. It complements—rather than replaces—the independent checksum comparison.

11. Decide whether the change is accepted

Record a one-paragraph decision in evidence/decision.txt with: median/representative serial-warm time, parallel-warm time, machine/JDK/Maven identity, thread count, warnings, artifact checksum result, and whether -T 2 is accepted. If timing is noisy or no faster, reject the change; evidence-backed optimization includes evidence-backed non-adoption.

12. Verification checklist

  • Wrapper reports Maven 3.9.16 and expected JDK 21.
  • All four modules appear in reactor evidence.
  • Graph prediction matches observed ordering constraints.
  • Cold/warm repository state is labeled separately.
  • Serial and parallel measurements use equivalent goals/project scope.
  • No unresolved non-thread-safe plugin warning is ignored.
  • Required JARs exist after serial and parallel builds.
  • Workspace A and B JAR checksums match by artifact.
  • artifact:check-buildplan evidence is preserved.
  • No normal user cache, production repository, credential, or signing material was changed.

13. Cleanup and rollback

cd ..
rm -rf maven-perf-checkpoint-a maven-perf-checkpoint-b

Run this only for the exact disposable directories created by the checkpoint. If you adapted names, verify them first. The rollback for the production decision is simpler: if -T 2 is not accepted, remove it from the workflow while leaving the project model unchanged.

Knowledge check

Why does the checkpoint use a 600 ms fixture in alpha and beta?

Why compare parallel-warm against serial-warm rather than serial-cold?

If -T 2 is faster but workspace B produces a different app JAR checksum, what is the acceptance decision?

Why is AntRun 3.2.0 acceptable for this synthetic fixture?

What if -T 2 is not faster on the learner machine?

What does the clean-room rebuild prove better than a second warm build in workspace A?

14. Production operating model and Chapter 14 bridge

Chapter 13 adds a rule to the build-engineering operating model: performance changes require controlled evidence and artifact invariants. You can now distinguish graph concurrency, process persistence, resolver state, reproducible archive controls, and diagnostic logging instead of treating them as one “Maven speed” feature.

Chapter 14 moves from optimizing the Maven 3 production baseline to evaluating Maven 4 preview behavior, compatibility, Build Consumer POM concepts, and a deliberate migration plan without confusing release-candidate software with the stable path.

Official references and version notes

Version-sensitive statements in this lesson were checked against current Apache Maven primary documentation on 2026-08-24.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.