# Maven 4 Immutable API: Thread-Safe Value Objects for Reliable Builds

> Discover Maven 4's immutable API: thread-safe value objects like Project and Dependency ensure reliable, side-effect-free builds, eliminating setters and mutable collections. Learn how it works.

- Repository: [The Apache Software Foundation/maven](https://github.com/apache/maven)
- Tags: internals
- Published: 2026-07-05

---

**Maven 4 introduces a purely immutable public API where all core model types—including Project, Artifact, Dependency, and XML nodes—are defined as @Immutable value objects that eliminate setters and mutable collections, ensuring thread-safe, side-effect-free data sharing across the build lifecycle.**

The Apache Maven project restructured its internal architecture in version 4 by replacing the mutable JavaBeans model used through Maven 3.10 with a strictly immutable API. This redesign affects everything from POM parsing to dependency resolution, utilizing generated value objects and read-only collections to prevent unintended side effects and enable safe parallel execution.

## What Is the Maven 4 Immutable API?

The Maven 4 immutable API is a complete redesign of the project's public interface that treats all data as immutable value objects. Every core model type—including `Project`, `Artifact`, `Dependency`, and the POM model itself—carries the `@Immutable` annotation defined in [`api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java`](https://github.com/apache/maven/blob/main/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java). This contract guarantees that once an instance is created, its state cannot change, making objects inherently thread-safe and safe to share across concurrent build components without defensive copying.

## How Immutable Value Objects Work

### Generated from the MDO Model

Unlike previous versions where model classes were hand-maintained, Maven 4 generates its immutable POM classes from the Maven MDO (`maven.mdo`) definition. The generated source lives in the `api/maven-api-model` module, as documented in [`api/maven-api-model/src/main/java/org/apache/maven/api/model/package-info.java`](https://github.com/apache/maven/blob/main/api/maven-api-model/src/main/java/org/apache/maven/api/model/package-info.java). This approach ensures consistency between the XML schema and the Java API while enforcing immutability at the code generation level.

### Immutable Collections Implementation

The API never exposes mutable Java collections. Instead, [`api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java`](https://github.com/apache/maven/blob/main/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java) provides fast, read-only implementations including `emptyMap()`, `singletonMap()`, and `copy(Collection)` methods. These return instances of `AbstractImmutableMap` and `AbstractImmutableSet` that throw `UnsupportedOperationException` on any mutation attempt. The `copy(Map)` implementation at lines 71-87 creates efficient read-only views without the overhead of defensive copies.

### Builder Pattern Construction

Since objects cannot be modified after creation, Maven 4 uses builders for complex construction. For example, `XmlNode` in [`api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java`](https://github.com/apache/maven/blob/main/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java) provides a `builder()` method that returns a mutable builder, but the `build()` method returns an immutable instance. The constructor at line 89 uses `ImmutableCollections.copy(...)` to store attributes and children, ensuring deep immutability.

## Thread Safety and Performance Benefits

Because model objects cannot change after construction, Maven components—including builders, resolvers, and plugins—can safely share them without explicit synchronization. This design eliminates an entire class of bugs caused by unintended side effects and simplifies caching and change detection in the new incremental build engine. The immutable API also provides a clean separation of data (immutable model objects) from behavior (services and components).

## Backward Compatibility

While Maven 4 uses the immutable model internally, the mutable Maven 3 model remains available for legacy code. The two models are kept strictly separate to prevent accidental mutation of data that the new engine relies on, allowing gradual migration of existing plugins and extensions.

## Working with the Immutable API

### Creating Immutable XML Nodes

Use the `XmlNode.builder()` to construct XML trees, then call `build()` to receive an immutable instance stored via `ImmutableCollections` in the internal implementation:

```java
// Build an XML node using the mutable builder
XmlNode node = XmlNode.builder()
    .name("project")
    .attribute("xmlns", "http://maven.apache.org/POM/4.0.0")
    .child(XmlNode.builder()
        .name("modelVersion")
        .text("4.0.0")
        .build())
    .build();   // ← returns an immutable XmlNode

// Attempting to modify throws UnsupportedOperationException
// node.attributes().put("new", "value");   // UOE

```

### Accessing Project Data

Objects returned by `ProjectBuilder` provide read-only views of collections backed by `ImmutableCollections.copy(...)`:

```java
// Project returned from ProjectBuilder
Project project = projectBuilder.build(...);

// Accessors provide immutable views
List<Artifact> artifacts = project.getArtifacts();      // immutable list
Map<String,String> properties = project.getProperties(); // immutable map

// artifacts.add(newArtifact);   // UnsupportedOperationException

```

### Using ImmutableCollections Directly

For custom implementations, use the utility class to wrap existing collections:

```java
Map<String,String> map = Map.of("a","1", "b","2");
Map<String,String> immutable = ImmutableCollections.copy(map);
// Returns AbstractImmutableMap - mutating methods throw UOE

```

## Key Source Files

- **[`api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java`](https://github.com/apache/maven/blob/main/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java)**: Defines the `@Immutable` annotation marking types as thread-safe and immutable.
- **[`api/maven-api-model/src/main/java/org/apache/maven/api/model/package-info.java`](https://github.com/apache/maven/blob/main/api/maven-api-model/src/main/java/org/apache/maven/api/model/package-info.java)**: Documents the immutable POM model generated from `maven.mdo`.
- **[`api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java`](https://github.com/apache/maven/blob/main/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlNode.java)**: Immutable XML node representation with the builder pattern.
- **[`api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java`](https://github.com/apache/maven/blob/main/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java)**: Fast read-only collections utility (lines 71-87 for `copy(Map)`).
- **[`api/maven-api-core/src/main/java/org/apache/maven/api/Project.java`](https://github.com/apache/maven/blob/main/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java)**: Immutable project model interface.
- **[`impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeImpl.java`](https://github.com/apache/maven/blob/main/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeImpl.java)**: Internal implementation using `ImmutableCollections` for storage.

## Summary

- **Maven 4 replaces mutable models** with `@Immutable` value objects for all core types including `Project`, `Artifact`, and `Dependency`.
- **Zero mutable collections** are exposed; the API uses `ImmutableCollections` to provide read-only maps and lists.
- **Builder pattern** is required for object construction—objects are immutable after calling `build()`.
- **Thread-safe by design** eliminates synchronization needs and enables safe parallel processing.
- **MDO-generated code** ensures the POM model matches the XML schema while enforcing immutability.

## Frequently Asked Questions

### What is the difference between Maven 3 and Maven 4 API models?

Maven 3 relies on mutable JavaBeans with setters and getters, while Maven 4 uses strictly immutable value objects. The Maven 4 API annotates all model types with `@Immutable` and removes all setter methods, replacing them with builder patterns and copy methods.

### How does the @Immutable annotation ensure thread safety?

The `@Immutable` annotation in [`api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java`](https://github.com/apache/maven/blob/main/api/maven-api-annotations/src/main/java/org/apache/maven/api/annotations/Immutable.java) marks classes as inherently thread-safe by contract. Combined with the implementation in classes like `XmlNode`, which uses `ImmutableCollections.copy(...)` to store all data, it guarantees that no state changes can occur after construction, allowing safe sharing across threads without synchronization.

### Can I modify a Maven 4 Project object after it is created?

No. Once constructed via `ProjectBuilder` or similar factories, `Project` objects are immutable. Any attempt to modify the returned collections—such as calling `add()` on `project.getArtifacts()` or `put()` on `project.getProperties()`—throws `UnsupportedOperationException`.

### Where are the immutable POM model classes generated from?

The immutable POM classes are generated from the `maven.mdo` model definition during the build process. The generated code resides in `api/maven-api-model` and is documented in [`package-info.java`](https://github.com/apache/maven/blob/main/package-info.java) as "Maven Immutable POM... generated from maven.mdo".