Final StretchOrdered learning track

Maven 4 Upgrade and New Model

Learn Maven In Action - Part 038

Maven 4 upgrade strategy, Maven 4 model changes, build POM vs consumer POM, subprojects, BOM packaging, migration tool, compatibility, and enterprise rollout.

13 min read2411 words
PrevNext
Lesson 3840 lesson track3440 Final Stretch
#maven#maven-4#migration#build-system+2 more

Part 038 — Maven 4 Upgrade and New Model

Maven 4 bukan sekadar “Maven 3 dengan versi baru”. Ia memperjelas pemisahan antara POM yang dipakai untuk build dan POM yang dikonsumsi downstream, memperbaiki model multi-project, memperketat validasi, dan membuka ruang fitur baru. Untuk enterprise, Maven 4 adalah migration program, bukan upgrade command.

Status faktual saat materi ini ditulis: dokumentasi rilis Maven menyatakan Maven 4.0+ masih “not yet GA”, dengan Maven 4.0.0-rc-5 sebagai release candidate yang membutuhkan Java 17 untuk menjalankan Maven. Maven 3.9.x tetap menjadi baseline produksi yang aman untuk banyak organisasi sampai Maven 4 GA dan kompatibilitas plugin/fleet tervalidasi.

Part ini mengajarkan cara berpikir tentang Maven 4:

  • apa yang berubah di model,
  • apa yang tetap kompatibel,
  • apa yang perlu dimigrasi,
  • bagaimana melakukan rollout aman,
  • bagaimana menghindari “upgrade theatre” yang hanya mengganti binary tanpa memahami behavior.

1. Core Mental Model: Maven 4 Separates Build Intent from Consumer Contract

Di Maven 3, POM yang ada di source control sering juga menjadi POM yang dipublikasikan dan dikonsumsi downstream. Akibatnya, satu file harus melayani dua audience:

  1. build system internal,
  2. downstream dependency consumer.

Masalahnya, build POM sering berisi hal yang tidak relevan untuk consumer:

  • plugin configuration,
  • build profiles,
  • properties internal,
  • generator setup,
  • test plugin setup,
  • repository/deployment detail,
  • corporate parent detail.

Maven 4 memperjelas konsep:

Mental model:

Build POM menjawab “bagaimana project ini dibangun”. Consumer POM menjawab “bagaimana project lain mengonsumsi artifact ini”.

Ini sangat penting untuk enterprise karena build internals tidak seharusnya bocor ke downstream dependency graph.


2. Maven 4 Compatibility Principle

Maven 4 tetap bisa membangun project Maven 3 dengan model version 4.0.0. Dokumentasi Maven 4 menyatakan tidak perlu mengubah build POM ke model 4.1.0 jika project tidak membutuhkan fitur baru.

Artinya migration strategy terbaik bukan:

Convert all POMs to 4.1.0 immediately.

Lebih baik:

1. Run existing Maven 3 projects under Maven 4 RC/GA in compatibility mode.
2. Fix validation and plugin compatibility issues.
3. Adopt new model features only where they solve concrete problems.
4. Publish and compare consumer POMs.
5. Roll out by repository class, not all at once.

3. Maven 4 Requires Java 17 Runtime

Maven runtime JDK berbeda dari project target JDK.

Maven 4 membutuhkan Java 17 untuk menjalankan Maven. Tetapi project yang dibangun bisa saja menargetkan Java lain melalui compiler --release atau toolchains, selama plugin dan toolchain setup mendukung.

Common misconception:

“Kalau Maven 4 butuh Java 17, semua aplikasi harus upgrade ke Java 17.”

Tidak selalu. Yang wajib Java 17 adalah runtime Maven, bukan otomatis bytecode aplikasi. Namun plugin lama, annotation processor lama, dan build script lama bisa tidak kompatibel dengan JDK 17 runtime. Itu harus diuji.

Enterprise implication:

  • CI image harus punya JDK 17 untuk menjalankan Maven 4.
  • Toolchains harus disiapkan jika project menargetkan Java 8/11/17/21 secara berbeda.
  • Plugin compatibility harus diuji dengan JDK 17 runtime.
  • Build wrapper harus mengunci Maven distribution yang dipakai.

4. Model Version 4.1.0

Maven 4 memperkenalkan model version 4.1.0 untuk build POM. Namespace-nya berbeda dari model lama.

Namun jangan mengubah model version hanya demi “terlihat modern”. Model version harus dinaikkan ketika kamu membutuhkan fitur model baru.

Decision table:

SituationKeep 4.0.0Move to 4.1.0
Project Maven 3 stableYesNo
Hanya ingin test Maven 4 runtimeYesNo
Ingin pakai <subprojects>NoYes
Ingin pakai packaging bom sebagai build POMNoYes
Ingin pakai Maven 4 model-only featureNoYes
Library dipakai banyak downstream lamaPrefer YesOnly after consumer POM validation

Contoh Maven 3-compatible POM tetap valid:

<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.acme.platform</groupId>
  <artifactId>billing-api</artifactId>
  <version>1.8.0</version>
</project>

Konsep Maven 4 build POM 4.1.0:

<project xmlns="http://maven.apache.org/POM/4.1.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd">
  <modelVersion>4.1.0</modelVersion>
  <groupId>com.acme.platform</groupId>
  <artifactId>platform-root</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <subprojects>
    <subproject>platform-bom</subproject>
    <subproject>billing-api</subproject>
    <subproject>billing-service</subproject>
  </subprojects>
</project>

The point is not syntax. The point is model semantics.


5. Modules Become Subprojects

Maven 3 documentation uses “multi-module”. Maven 4 documentation moves toward “multi-project” and “subprojects”. The old <modules> element remains usable, but Maven 4 model 4.1.0 introduces <subprojects>.

Why this matters:

  • “module” is overloaded in Java because Java Platform Module System also uses module.
  • “subproject” better describes independent Maven projects inside one build tree.
  • It clarifies that reactor builds projects, not Java modules.

Mental model:

Maven reactor still performs the same conceptual jobs:

  1. collect subprojects,
  2. sort build order,
  3. select subprojects,
  4. build selected subprojects.

The migration is more about clearer language and model evolution than abandoning reactor reasoning.


6. New bom Packaging

In Maven 3, BOMs are usually pom packaging with dependencyManagement. This works, but semantically a parent POM and a BOM both look like packaging pom.

Maven 4 introduces dedicated bom packaging in the new model to separate roles:

  • parent POM: inheritance/build policy,
  • aggregator POM: project collection,
  • BOM: dependency version alignment.

This is a good correction because many enterprise Maven systems accidentally merge these roles.

Old pattern:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.acme.platform</groupId>
  <artifactId>acme-platform-bom</artifactId>
  <version>2026.07.0</version>
  <packaging>pom</packaging>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

Maven 4 model direction:

<project xmlns="http://maven.apache.org/POM/4.1.0">
  <modelVersion>4.1.0</modelVersion>
  <groupId>com.acme.platform</groupId>
  <artifactId>acme-platform-bom</artifactId>
  <version>2026.07.0</version>
  <packaging>bom</packaging>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

Enterprise rule:

Maven 4 bom packaging is a chance to clean architecture, not a reason to rename every POM.

Adopt it first in platform/BOM repositories where the semantic win is clear.


7. Consumer POM: Why It Matters

A downstream project usually needs dependency metadata, not your build machinery.

Consumer POM should preserve what consumers need:

  • coordinates,
  • dependency graph information,
  • dependency management where relevant,
  • project metadata needed for consumption,
  • repository information where appropriate.

Consumer POM should not force downstream consumers to understand:

  • your compiler plugin config,
  • your test plugin config,
  • your internal source generation setup,
  • your build-only properties,
  • your CI-only profiles.

This reduces accidental coupling.

Failure Mode Maven 4 Tries to Reduce

Company publishes library com.acme:case-client with a POM that leaks internal plugin/profile/build properties. Downstream service imports it and suddenly model resolution or profile interpolation behaves strangely.

Root problem:

build-only details leaked into consumer contract.

Consumer POM separation helps reduce that class of leak.


8. Stricter Validation

Maven 4 is expected to be stricter about model correctness and plugin validation. This is good, but it can surprise old builds that have accumulated warnings for years.

Common categories:

  • missing plugin versions,
  • malformed POM elements,
  • duplicate dependency declarations,
  • deprecated model features,
  • ambiguous profile activation,
  • invalid parent/relativePath setup,
  • plugin using internal Maven APIs,
  • extensions relying on Maven 3 internals.

Upgrade mindset:

Treat Maven 4 warnings as migration inventory, not noise.

Pipeline approach:

# First inventory, do not immediately enforce fleet-wide
mvn -V -e -DskipTests validate
mvn -V -e help:effective-pom
mvn -V -e dependency:tree

Then classify warnings:

Warning TypeAction
Missing plugin versionPin in parent pluginManagement
Duplicate dependencyRemove duplicate or move version to BOM
Deprecated Maven 4 model featurePlan POM migration
Plugin validation warningUpgrade plugin or report issue
Extension compatibility warningTest new extension version
Consumer POM warningCompare published POM metadata

9. Maven Upgrade Tool

Maven 4 release candidate documentation mentions a built-in migration/upgrade tool beginning from the RC line. Treat such tool as an assistant, not an authority.

Safe use pattern:

1. Create migration branch.
2. Run upgrade tool on a small representative repository.
3. Review diff manually.
4. Run Maven 3 build if compatibility is expected.
5. Run Maven 4 build.
6. Compare effective POM.
7. Compare dependency tree.
8. Compare produced artifact checksum/content.
9. Compare published consumer POM.
10. Only then generalize.

Never run an upgrade tool across hundreds of repos and auto-merge the result.


10. Enterprise Migration Strategy

Do not migrate by enthusiasm. Migrate by risk class.

10.1 Repository Classes

ClassExampleRiskMigration Order
Build playgroundsample/internal experimentsLow1
Leaf serviceinternal deployable, few consumersMedium2
Internal libraryconsumed by several servicesMedium-high3
Platform BOMimported by many projectsHigh4
Corporate parent POMinherited by many projectsVery high5
Custom Maven plugin/extensionchanges build behaviorVery highParallel validation
Public artifactexternal consumersCriticalLast

10.2 Migration Phases

10.3 Inventory Script Ideas

At minimum collect:

find . -name pom.xml -maxdepth 5 -print
mvn -version
mvn -q help:effective-pom -Doutput=target/effective-pom.xml
mvn -q help:effective-settings -Doutput=target/effective-settings.xml
mvn -q dependency:tree -DoutputFile=target/dependency-tree.txt

Fleet-level inventory should detect:

  • Maven wrapper version,
  • parent POM coordinates,
  • plugin versions,
  • plugin without version,
  • Maven Enforcer rules,
  • .mvn/extensions.xml,
  • .mvn/maven.config,
  • use of system scope,
  • use of LATEST/RELEASE,
  • custom repositories in POM,
  • old plugins using Maven internals,
  • custom packaging,
  • custom lifecycle extension,
  • release plugin usage,
  • flatten plugin usage,
  • CI-friendly versions usage.

11. Maven Wrapper Strategy

For enterprise migration, Maven Wrapper is not optional hygiene; it is build runtime pinning.

Bad:

mvn verify

Better:

./mvnw -V verify

Why:

  • developer and CI use same Maven distribution,
  • upgrade can be branch-specific,
  • canary projects can move first,
  • rollback is changing wrapper distribution, not reinstalling global Maven.

Suggested rollout:

Phase 1: Ensure all repos have Maven Wrapper.
Phase 2: Pin Maven 3.9.x latest approved baseline.
Phase 3: Add optional Maven 4 CI job, non-blocking.
Phase 4: Make Maven 4 CI blocking for canary repos.
Phase 5: Roll to leaf services.
Phase 6: Roll to libraries.
Phase 7: Roll to parent/BOM repos.

12. Plugin Compatibility

Maven core and Maven plugins are released independently. Upgrading Maven does not automatically upgrade plugins.

Migration checklist:

[ ] Pin all plugin versions.
[ ] Upgrade ancient plugins before Maven 4 migration.
[ ] Remove plugins using unsupported internal assumptions.
[ ] Test plugin goals used in lifecycle, not only mvn validate.
[ ] Test site/report plugins if organization uses them.
[ ] Test release/deploy flows separately.
[ ] Test custom plugins with Maven Plugin Testing/Invoker.

High-risk plugins:

  • old release plugin configuration,
  • old compiler/surefire/failsafe plugins,
  • custom packaging plugins,
  • plugins with <extensions>true</extensions>,
  • plugins that inspect MavenProject internals,
  • custom company plugins,
  • old codegen plugins,
  • site/reporting plugins.

13. Extension Compatibility

Part 037 matters here. Extensions are more sensitive than plugins because they run closer to Maven runtime.

Check:

find .mvn -type f -maxdepth 2 -print
cat .mvn/extensions.xml
mvn -X validate

If you have internal extension:

  • build it and test it with Maven 3.9.x,
  • build it and test it with Maven 4 RC/GA,
  • verify classloading behavior,
  • test parallel build,
  • test clean-room repository,
  • test offline mode,
  • test failure handling.

Do not roll Maven 4 globally until extension compatibility is proven.


14. Consumer POM Comparison Workflow

For a library or BOM, the key question is not “does mvn package pass?”

The key question is:

Does the artifact published under Maven 4 mean the same thing to consumers?

Workflow:

# Maven 3 build
./mvnw -V -DskipTests clean deploy -DaltDeploymentRepository=local::file:target/repo-m3

# Maven 4 build, using Maven 4 wrapper branch or explicit wrapper
./mvnw -V -DskipTests clean deploy -DaltDeploymentRepository=local::file:target/repo-m4

# Compare generated POMs
find target/repo-m3 -name "*.pom" -print | sort > target/poms-m3.txt
find target/repo-m4 -name "*.pom" -print | sort > target/poms-m4.txt

Then compare:

  • coordinates,
  • dependency declarations,
  • dependencyManagement,
  • optional/exclusions,
  • scopes,
  • repositories,
  • parent references,
  • properties that consumers rely on,
  • relocation/distribution metadata.

For platform BOMs, also create a downstream fixture project that imports the BOM and resolves dependency tree.

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.acme.platform</groupId>
      <artifactId>acme-platform-bom</artifactId>
      <version>2026.07.0-maven4-candidate</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Then:

mvn -U dependency:tree
mvn -U help:effective-pom

15. Build POM Cleanup Before Maven 4

Maven 4 migration is easier if Maven 3 builds are already clean.

Clean these first:

15.1 Pin Plugin Versions

Bad:

<plugin>
  <artifactId>maven-compiler-plugin</artifactId>
</plugin>

Good:

<pluginManagement>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>${maven.compiler.plugin.version}</version>
    </plugin>
  </plugins>
</pluginManagement>

15.2 Remove Dynamic Versions

Bad:

<version>LATEST</version>

Bad for release:

<version>1.2.3-SNAPSHOT</version>

Good:

<version>1.2.3</version>

15.3 Separate Parent, Aggregator, BOM

Bad:

one root POM does everything:
- parent inheritance
- module aggregation
- BOM version alignment
- release metadata
- plugin policy
- repository policy

Better:

platform-root/         aggregator
platform-parent/       inherited build policy
platform-bom/          dependency version alignment
service-a/             deployable
library-b/             reusable artifact

15.4 Remove Environment-Specific Build Outputs

Bad:

mvn package -Pprod
mvn package -Pqa

producing different artifacts for each environment.

Better:

same artifact + runtime configuration injection

16. CI Rollout Pattern

Example GitHub Actions matrix concept:

name: Maven Compatibility

on:
  pull_request:
  push:
    branches: [ main ]

jobs:
  maven3:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'
          cache: maven
      - run: ./mvnw -V -B verify

  maven4-compat:
    runs-on: ubuntu-latest
    continue-on-error: true
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'
          cache: maven
      - run: ./mvnw -V -B -DskipITs verify

Eventually:

maven4-compat non-blocking -> blocking for canary -> blocking for fleet -> default wrapper.

17. Rollback Strategy

A serious Maven 4 rollout must have rollback.

Rollback surfaces:

SurfaceRollback
Maven wrapperrevert wrapper distribution URL/version
CI imagepin previous image tag
Parent POMrelease previous parent version
BOMrelease patched old BOM line
Plugin upgraderevert pluginManagement version
Extensiondisable .mvn/extensions.xml or downgrade extension
Model 4.1.0 migrationrevert POM model change if not required
Consumer POM issuestop publish, restore last known good artifact

Never combine all of these in one release.

Bad migration PR:

- Upgrade Maven to 4
- Upgrade Java from 11 to 21
- Rewrite parent POM
- Change BOM structure
- Change CI image
- Upgrade all plugins
- Convert modules to subprojects
- Enable build cache

Good migration sequence:

PR 1: Pin plugin versions and remove warnings under Maven 3.
PR 2: Upgrade CI JDK runtime to 17 while still using Maven 3.
PR 3: Add Maven 4 non-blocking compatibility job.
PR 4: Fix plugin/extension warnings.
PR 5: Make Maven 4 blocking for one canary service.
PR 6: Roll wrapper to Maven 4 for low-risk repos.
PR 7: Adopt Maven 4 model features selectively.

18. Migration Failure Playbook

Case 1: Build Fails Before Project Starts

Likely:

  • Maven runtime JDK wrong,
  • wrapper downloads wrong distribution,
  • .mvn/extensions.xml incompatible,
  • repository cannot resolve core extension.

Commands:

./mvnw -version
java -version
cat .mvn/extensions.xml || true
./mvnw -e -X validate

Case 2: Plugin Fails Under Maven 4

Likely:

  • old plugin uses Maven internals,
  • plugin needs newer version,
  • plugin dependency conflict,
  • JDK 17 runtime incompatibility.

Commands:

./mvnw -e -X -DskipTests validate
./mvnw help:effective-pom -Doutput=target/effective-pom.xml

Action:

  • upgrade plugin,
  • pin plugin version,
  • isolate plugin execution,
  • create minimal reproduction.

Case 3: Tests Fail Only Under Maven 4

Likely:

  • Surefire/Failsafe version old,
  • JDK 17 runtime differences,
  • argLine/module options,
  • toolchain not applied,
  • environment differences.

Commands:

./mvnw -V -Dtest=SpecificTest test
./mvnw -V -DskipITs=false verify

Case 4: Artifact Differs

Likely:

  • consumer POM generation changed,
  • archive timestamp/config changed,
  • plugin version changed,
  • generated source changed,
  • build profile activation changed.

Commands:

jar tf target/*.jar | sort > target/jar-contents.txt
sha256sum target/*.{jar,pom} || true

Case 5: Downstream Cannot Consume Artifact

Likely:

  • consumer POM changed,
  • dependencyManagement lost property,
  • scope/exclusion changed,
  • parent reference leaked,
  • repository metadata issue.

Action:

  • compare published POM,
  • create downstream fixture,
  • resolve dependency tree under Maven 3 and Maven 4,
  • block release if semantic difference is not intentional.

19. When to Adopt Maven 4 Features

Adoption should be problem-driven.

Feature AreaAdopt WhenAvoid When
Maven 4 runtimeYou need compatibility/future readinessCritical release window without validation
Model 4.1.0You need new model featuresProject builds fine and downstream compatibility is priority
<subprojects>You want clearer multi-project modelTeam/tooling still assumes <modules> heavily
bom packagingYou operate serious platform BOMsYou have simple single-project apps
Consumer POM behaviorYou publish libraries/BOMsInternal deployable only, no downstream consumption
Upgrade toolYou want guided cleanupYou cannot review generated diffs

20. Enterprise Maven 4 Readiness Checklist

Runtime
[ ] CI can run Java 17 for Maven runtime.
[ ] Maven Wrapper exists in all repos.
[ ] Maven 3 baseline is current and supported.
[ ] Maven 4 candidate version is pinned, not floating.

POM Hygiene
[ ] Plugin versions pinned.
[ ] Parent/BOM/aggregator roles separated where necessary.
[ ] Dynamic versions removed.
[ ] System scope removed or explicitly justified.
[ ] Custom repositories audited.
[ ] Profiles audited.

Plugins
[ ] Compiler plugin works under Maven 4 runtime.
[ ] Surefire/Failsafe work under Maven 4 runtime.
[ ] Deploy/release plugins tested.
[ ] Codegen plugins tested.
[ ] Site/reporting plugins tested if used.
[ ] Custom plugins tested.

Extensions
[ ] .mvn/extensions.xml inventoried.
[ ] Build extensions inventoried.
[ ] Plugin extensions=true inventoried.
[ ] Internal extensions tested against Maven 4.
[ ] Escape hatch documented.

Artifacts
[ ] JAR/WAR content compared.
[ ] Generated POM compared.
[ ] BOM import fixture tested.
[ ] Downstream dependency tree tested.
[ ] Reproducibility checked.

Rollout
[ ] Canary repos selected.
[ ] Non-blocking Maven 4 CI job exists.
[ ] Rollback plan exists.
[ ] Migration diff reviewed.
[ ] Release freeze avoided.

21. Senior Engineer Heuristic

Maven 4 migration is not about chasing the newest version. It is about strengthening the build contract.

Ask these questions:

  1. Did the migration make build behavior more explicit?
  2. Did it reduce build-only metadata leaking to consumers?
  3. Did it preserve downstream dependency semantics?
  4. Did it reduce warnings or merely hide them?
  5. Did it separate runtime Maven upgrade from application Java upgrade?
  6. Did it give CI and developers the same Maven runtime?
  7. Can we rollback one layer at a time?

If the answer is no, the migration is not done.


22. What You Should Be Able to Do Now

After this part, you should be able to:

  • explain Maven 4 as a model/runtime evolution,
  • distinguish Maven runtime JDK from project target JDK,
  • decide whether to keep model 4.0.0 or adopt 4.1.0,
  • understand build POM vs consumer POM,
  • reason about <modules> vs <subprojects>,
  • use Maven 4 bom packaging selectively,
  • build an enterprise Maven 4 migration plan,
  • test plugin/extension compatibility,
  • compare artifacts and consumer POMs across Maven versions,
  • design rollback paths.

Part berikutnya akan membandingkan Maven dengan Gradle, Bazel, dan build-system lain. Fokusnya bukan debat fanboy tool, tetapi tradeoff arsitektural: determinism, convention, extensibility, performance, enterprise governance, developer experience, dan organizational fit.


References

Lesson Recap

You just completed lesson 38 in final stretch. 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.