If you’ve been building Java projects for any length of time, Maven has probably been your trusty companion — that reliable friend who shows up every day, does the job, and never asks for anything in return. Maven 3 dropped back in 2010, and since then, we’ve seen Java evolve through a dozen major versions, containers take over the world, and microservices become everyone’s favorite architecture pattern. Meanwhile, Maven just kept chugging along with the same POM model it’s had since the Bush administration.
Well, the wait is over. Maven 4 is here, and it’s not just a minor facelift — it’s the most significant overhaul the build tool has seen in over a decade. Let’s dig into what’s changed and why you should care.
The Big Picture: Why Maven 4 Matters
Maven has been the backbone of Java builds for over 20 years, and one thing has remained sacred: the pom.xml with Model Version 4.0.0. That stability was both a blessing and a curse. Sure, your decade-old projects still build, but it also meant Maven couldn’t evolve without risking the entire ecosystem.
Maven 4 finally breaks free from that amber prison. The new version brings a clearer POM model, better performance for multi-project builds, and modernized dependency resolution — all while maintaining backward compatibility where it counts.
Build POM vs Consumer POM: The Separation We Needed
Here’s something that always bugged experienced Maven users: when you published a library, your POM file included all sorts of build-specific garbage that downstream consumers didn’t need. Plugin configurations, build profiles, repository declarations — all of it got shipped to Maven Central and cluttered up dependency resolution.
Maven 4 introduces a clean separation between the Build POM and the Consumer POM:
- Build POM: This is your regular
pom.xmlwith all the build configuration, plugins, and profiles you need during development. - Consumer POM: Maven automatically generates a stripped-down version during the build that only contains what other projects actually need — dependencies and metadata.
The consumer POM excludes:
- Parent POM references (everything is resolved and flattened)
- Plugin configurations
- Build profiles
- Properties (resolved in place)
- Unused dependencies
In Maven 3, you needed the Flatten Maven Plugin to achieve something similar. Now it’s built-in:
mvn clean install -Dmaven.consumer.pom.flatten=true
This is a game-changer for library authors. Your consumers get a cleaner dependency tree, and you don’t leak internal implementation details.
Model Version 4.1.0: New Tricks for the POM
While Maven 4 happily builds projects using the classic 4.0.0 model, you can opt into the new Model Version 4.1.0 to unlock additional features. The consumer POM is still generated as 4.0.0 for compatibility, so nothing breaks downstream.
Here’s what 4.1.0 brings to the table:
Automatic Parent Versioning
This one has been requested since 2005 (yes, really — check out MNG-624 in the issue tracker). When using model version 4.1.0, you no longer need to specify version information in child modules:
<project xmlns="http://maven.apache.org/POM/4.1.0">
<modelVersion>4.1.0</modelVersion>
<parent>
<!-- No groupId, artifactId, or version needed! -->
<relativePath>..</relativePath>
</parent>
<artifactId>my-module</artifactId>
</project>
Maven infers everything from the parent. Dependencies on sibling projects in the same reactor also don’t need explicit versions anymore.
Subprojects Replace Modules
Remember the confusion when Java 9 introduced the Java Platform Module System, and suddenly “modules” meant two different things? Maven 4 fixes that terminology collision by renaming “modules” to “subprojects”:
<subprojects>
<subproject>project-a</subproject>
<subproject>project-b</subproject>
</subprojects>
The modules element still works (it’s just deprecated), so your existing POMs won’t break.
Subproject Auto-Discovery
Even better — you can omit the subprojects element entirely, and Maven will automatically discover all subdirectories containing a pom.xml. Less boilerplate, fewer merge conflicts when adding new modules.
Dedicated BOM Packaging
Maven 4 introduces a proper bom packaging type to differentiate between parent POMs and Bill of Materials POMs:
<packaging>bom</packaging>
This creates a cleaner separation of concerns — your BOM is explicitly a dependency-managing artifact, not a parent with build configuration.
Tree-Based Lifecycle: Faster Multi-Project Builds
In Maven 3, the lifecycle was a simple ordered list of phases. Every project had to complete a phase before dependent projects could start building. For large multi-module projects, this was a bottleneck.
Maven 4 reimagines the lifecycle as a tree of phases. Each project moves through phases independently, and dependent projects can start as soon as their dependencies reach the “ready” phase.
To enable this concurrent execution:
mvn verify -b concurrent
The result? Significantly faster builds for multi-module projects, especially on machines with multiple CPU cores.
Lifecycle Hooks: Precise Control Over Build Phases
Maven 4 introduces before: and after: hooks for every lifecycle phase, replacing the inconsistent pre-* and post-* phases from Maven 3:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<phase>before:install</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
This is semantically cleaner than binding to verify when you really want something to run just before install. The old pre-integration-test and post-integration-test phases now act as aliases for before:integration-test and after:integration-test.
Maven 4 also adds special phases for multi-project builds:
before:all— Runs before any phase in the current projectafter:all— Runs at the very end of a project’s buildbefore:eachandafter:each— Wrap the standard lifecycle phases of individual subprojects
Better Annotation Processor Support
JDK 23 disabled automatic annotation processor discovery for security reasons (no more scanning the classpath for processors). Maven 4, combined with the new Maven Compiler Plugin 4.x, makes processor configuration simpler with new dependency types:
<dependencies>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-processor</artifactId>
<version>${version.hibernate}</version>
<type>processor</type>
</dependency>
</dependencies>
You can also explicitly control whether a processor goes on the classpath or module path:
processor— Maven guesses the appropriate pathclasspath-processor— Explicitly place on the processor classpathmodular-processor— Explicitly place on the processor module path
The same logic applies to regular JARs with classpath-jar and module-jar types, giving you full control over how dependencies are handled in modular builds.
Conditional Profile Activation
Profiles in Maven 4 can now use complex expressions with a new condition element:
<profiles>
<profile>
<id>conditional-profile</id>
<activation>
<condition><![CDATA[
exists('${project.basedir}/src/**/*.xsd') && length(${user.name}) > 5
]]></condition>
</activation>
</profile>
</profiles>
This is much more powerful than the old property-based activation, letting you combine multiple conditions with logical operators.
Also new: activating a non-existent profile with -PmyProfile now fails the build. Use -P?myProfile to make it optional.
The Maven Upgrade Tool: Your Migration Helper
Worried about migrating your existing projects? Maven 4 ships with a built-in upgrade tool called mvnup that scans your POMs, plugins, and structure, then recommends (or applies) updates.
Check what changes are needed without modifying anything:
mvnup check --model-version 4.1.0 --all
Apply all recommended upgrades:
mvnup apply --model-version 4.1.0 --all
You can also be selective:
# Only upgrade plugins
mvnup apply --plugins
# Only fix Maven 4 incompatibilities
mvnup apply --model
# Remove redundant information that Maven can infer
mvnup apply --infer
The tool handles common migration tasks like:
- Fixing unsupported
combine.childrenandcombine.selfattributes - Removing duplicate dependencies in
<dependencyManagement> - Creating the
.mvndirectory for root detection - Trimming redundant parent element information
- Upgrading plugins to Maven 4-compatible versions
Java 17 Required (For Maven, Not Your Code)
Here’s something that might catch you off guard: Maven 4 requires Java 17 to run. But don’t panic — this is only for Maven itself. You can still compile your code against older Java versions using the Toolchains feature:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
The JDK 17 requirement lets Maven’s internals use modern Java features like sealed classes, records, and improved HTTP clients.
Performance Improvements
Beyond the tree-based lifecycle, Maven 4 includes the new Resolver 2.0 library with over 150 improvements for dependency resolution. The resolver is now hidden behind a proper API, giving plugin developers a stable interface.
For even faster builds, you can use:
- Maven Daemon (mvnd): Keeps a pool of ready Maven processes for near-instant startup
- Maven Shell (mvnsh): A new shell that keeps a single Maven process running for interactive use
Smaller Quality-of-Life Improvements
Maven 4 is packed with thoughtful improvements:
- Consistent timestamps: All subproject archives get the same timestamp for reproducible builds
- Safe deployment: If one subproject fails, others aren’t deployed to repositories
- Fail on severity: Use
--fail-on-severity WARNto fail builds on warnings - Smart resume: The
-rflag intelligently analyzes the dependency graph to resume failed builds - New directory properties:
${session.topDirectory},${project.rootDirectory}, and${session.rootDirectory}give you better control over path resolution
Migration Strategy: Take It Step by Step
The Maven team recommends a three-step migration:
- Verify with Maven 3.9: Make sure your project builds cleanly with the latest Maven 3.x
- Upgrade plugins: Use the Versions Maven Plugin to update to the latest Maven 3-compatible plugin versions
- Switch to Maven 4: Install Maven 4 and run
mvnup checkto identify any issues
You don’t have to update your POMs to 4.1.0 immediately. Maven 4 happily builds 4.0.0 projects — the new features are opt-in.
The Bottom Line
Maven 4 represents 15 years of accumulated wisdom about what developers actually need from a build tool. The separated Build/Consumer POM model is elegant. The tree-based lifecycle makes multi-module builds faster. The upgrade tool means migration isn’t a leap of faith.
Most importantly, the Maven team has prioritized backward compatibility. Your existing projects should build with minimal changes, and you can adopt new features at your own pace.
Is it perfect? No. You’ll still write XML (though HOCON support is available via extensions). You’ll still occasionally wonder why dependency:tree shows something unexpected. But Maven 4 is a solid foundation for the next decade of Java builds.
Time to update that CI pipeline.
Sources: