| name | 110-java-maven-best-practices | ||||
|---|---|---|---|---|---|
| description | Use when you need to improve your Maven pom.xml using best practices. | ||||
| license | Apache-2.0 | ||||
| metadata |
|
You are a Senior software engineer with extensive experience in Java software development
Effective Maven usage involves robust dependency management via <dependencyManagement> and BOMs, adherence to the standard directory layout, and centralized plugin management. Build profiles should be used for environment-specific configurations. POMs must be kept readable and maintainable with logical structure and properties for versions. Custom repositories should be declared explicitly and their use minimized, preferably managed via a central repository manager.
Maven is built on several foundational principles that guide its design and usage:
1. Convention Over Configuration: Maven follows the principle that sensible defaults should be provided so developers don't need to specify everything explicitly. The standard directory layout (src/main/java, src/test/java) exemplifies this - Maven knows where to find source code without explicit configuration.
2. Declarative Project Model: Projects are described through a declarative Project Object Model (POM) rather than imperative build scripts. You declare what you want (dependencies, plugins, goals) rather than how to achieve it.
3. Dependency Management and Transitive Dependencies: Maven automatically resolves and downloads dependencies and their transitive dependencies, creating a complete classpath. The dependency management system prevents version conflicts through nearest-wins and dependency mediation strategies.
4. Build Lifecycle and Phases: Maven follows a well-defined build lifecycle with standard phases (validate, compile, test, package, install, deploy). This provides predictability and consistency across all Maven projects.
5. Plugin-Based Architecture: All Maven functionality is provided through plugins. Core operations like compilation, testing, and packaging are all plugin-based, making Maven extensible and modular.
6. Repository-Centric: Maven uses repositories (local, central, remote) as the primary mechanism for sharing and reusing artifacts. This enables easy sharing of libraries and promotes reuse across the Java ecosystem.
7. Coordinate System: Every artifact is uniquely identified by coordinates (groupId, artifactId, version), enabling precise dependency specification and avoiding JAR hell.
8. Inheritance and Aggregation: Projects can inherit from parent POMs (inheritance) and contain multiple modules (aggregation), enabling both shared configuration and multi-module builds.
In multi-module projects, best-practices analysis must extend beyond the root POM to cover all child module POMs. This includes verifying the correctness of the full inheritance chain, detecting cross-module version drift, eliminating redundant <dependencyManagement> or <pluginManagement> blocks in child modules, and ensuring that shared properties remain centralized in the parent or a dedicated BOM module.
Before applying Maven best practices recommendations, ensure the project is in a valid state by running Maven validation. This helps identify any existing configuration issues that need to be resolved first. For multi-module projects, scope analysis must cover every child module POM — not just the root.
- MANDATORY: Run
./mvnw validateormvn validatebefore applying any Maven best practices recommendations - VERIFY: Ensure all validation errors are resolved before proceeding with POM modifications
- SAFETY: If validation fails, not continue and ask the user to fix the issues before continuing
- MULTI-MODULE DISCOVERY: After reading the root
pom.xml, check whether it contains a<modules>section. If it does, read every child module'spom.xmlbefore making any recommendations — analysis scope is the full module tree, not only the root - CROSS-MODULE SCOPE: When child modules exist, check each one for: hardcoded dependency versions that duplicate
<dependencyManagement>in the parent, plugin configurations that duplicate<pluginManagement>, properties that should be centralized in the parent, and version drift (same artifact declared at different versions across sibling modules)
- Example 1: Effective Dependency Management
- Example 2: Standard Directory Layout
- Example 3: Plugin Management and Configuration
- Example 4: Use Build Profiles for Environment-Specific Configurations
- Example 5: Keep POMs Readable and Maintainable
- Example 6: Manage Repositories Explicitly
- Example 7: Centralize Version Management with Properties
- Example 8: Multi-Module Project Structure
- Example 9: Cross-Module Version Consistency
Title: Manage Dependencies Effectively using dependencyManagement and BOMs
Description: Use the <dependencyManagement> section in parent POMs or import Bill of Materials (BOMs) to centralize and control dependency versions. This helps avoid version conflicts and ensures consistency across multi-module projects. Avoid hardcoding versions directly in <dependencies> when managed elsewhere.
Good example:
<!-- Parent POM -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<spring.version>5.3.23</spring.version>
<junit.version>5.9.0</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Import a BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<!-- Child POM -->
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>my-module</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<!-- Version is inherited from parent's dependencyManagement -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<!-- Version and scope inherited -->
</dependency>
</dependencies>
</project>
Bad example:
<!-- Child POM hardcoding versions -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-other-module</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version> <!-- Hardcoded, may differ from parent's intention -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version> <!-- Different version, potential conflict -->
<scope>test</scope>
</dependency>
</dependencies>
</project>
Title: Adhere to the Standard Directory Layout
Description: Follow Maven's convention for directory structure (src/main/java, src/main/resources, src/test/java, src/test/resources, etc.). This makes projects easier to understand and build, as Maven relies on these defaults.
Good example:
my-app/
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── com/example/myapp/App.java
│ └── resources/
│ └── application.properties
└── test/
├── java/
│ └── com/example/myapp/AppTest.java
└── resources/
└── test-data.xml
Bad example:
my-app/
├── pom.xml
├── sources/ <!-- Non-standard -->
│ └── com/example/myapp/App.java
├── res/ <!-- Non-standard -->
│ └── config.properties
└── tests/ <!-- Non-standard -->
└── com/example/myapp/AppTest.java
<!-- This would require explicit configuration in pom.xml to specify source/resource directories -->
Title: Manage Plugin Versions and Configurations Centrally
Description: Use <pluginManagement> in a parent POM to define plugin versions and common configurations. Child POMs can then use the plugins without specifying versions, ensuring consistency. Override configurations in child POMs only when necessary.
Good example:
<!-- Parent POM -->
<project>
<!-- ... -->
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
<!-- Child POM -->
<project>
<!-- ... -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- Version and basic configuration inherited -->
</plugin>
</plugins>
</build>
</project>
Bad example:
<!-- Child POM -->
<project>
<!-- ... -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <!-- Different version, potentially older/incompatible -->
<configuration>
<source>11</source> <!-- Different configuration -->
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Title: Employ Build Profiles for Environment-Specific Settings Description: Use Maven profiles to customize build settings for different environments (e.g., dev, test, prod) or other conditional scenarios. This can include different dependencies, plugin configurations, or properties. Activate profiles via command line, OS, JDK, or file presence.
Good example:
<project>
<!-- ... -->
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<database.url>jdbc:h2:mem:devdb</database.url>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<database.url>jdbc:postgresql://prodserver/mydb</database.url>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>run</goal></goals>
<configuration>
<target>
<!-- Minify JS/CSS for prod -->
<echo>Simulating minification for prod</echo>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
<!-- Activation: mvn clean install -P prod -->
Bad example:
<!-- Commented out sections for different environments -->
<project>
<!-- ... -->
<properties>
<!-- <database.url>jdbc:h2:mem:devdb</database.url> -->
<database.url>jdbc:postgresql://prodserver/mydb</database.url> <!-- Manually switch by commenting/uncommenting -->
</properties>
</project>
Title: Structure POMs Logically for Readability
Description: Organize your pom.xml sections in a consistent order (e.g., project coordinates, parent, properties, dependencyManagement, dependencies, build, profiles, repositories). Use properties for recurring versions or values. Add comments for complex configurations.
Good example:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Project Coordinates -->
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>My Application</name>
<description>A sample application.</description>
<!-- Parent (if any) -->
<!-- ... -->
<!-- Properties -->
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<some.library.version>2.5.1</some.library.version>
</properties>
<!-- Dependency Management -->
<dependencyManagement>
<!-- ... -->
</dependencyManagement>
<!-- Dependencies -->
<dependencies>
<dependency>
<groupId>org.some.library</groupId>
<artifactId>some-library-core</artifactId>
<version>${some.library.version}</version>
</dependency>
<!-- ... -->
</dependencies>
<!-- Build Configuration -->
<build>
<!-- ... -->
</build>
<!-- Profiles (if any) -->
<!-- ... -->
<!-- Repositories and Plugin Repositories (if needed) -->
<!-- ... -->
</project>
Bad example:
<!-- Haphazard order, missing properties for versions -->
<project>
<dependencies>
<dependency>
<groupId>org.some.library</groupId>
<artifactId>some-library-core</artifactId>
<version>2.5.1</version> <!-- Version hardcoded, repeated elsewhere -->
</dependency>
</dependencies>
<modelVersion>4.0.0</modelVersion>
<build>
<!-- ... -->
</build>
<groupId>com.example</groupId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<artifactId>my-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
</project>
Title: Declare Custom Repositories Explicitly and Minimize Their Use
Description: Prefer dependencies from Maven Central. If custom repositories are necessary, declare them in the <repositories> section and <pluginRepositories> for plugins. It's often better to manage these in a company-wide Nexus/Artifactory instance configured in settings.xml rather than per-project POMs. Avoid relying on transitive repositories.
Good example:
<project>
<!-- ... -->
<repositories>
<repository>
<id>my-internal-repo</id>
<url>https://nexus.example.com/repository/maven-releases/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>my-internal-plugins</id>
<url>https://nexus.example.com/repository/maven-plugins/</url>
</pluginRepository>
</pluginRepositories>
</project>
<!-- Better: Configure these in settings.xml and use a repository manager -->
Bad example:
<!-- No explicit repository for a non-central artifact, relying on developer's local settings or transitive ones -->
<project>
<!-- ... -->
<dependencies>
<dependency>
<groupId>com.internal.stuff</groupId>
<artifactId>internal-lib</artifactId>
<version>1.0</version>
<!-- If this is not in Maven Central, the build will fail unless
the repository is configured in settings.xml or a parent POM.
Relying on implicit configurations makes builds less portable. -->
</dependency>
</dependencies>
</project>
Title: Use Properties to Manage Dependency and Plugin Versions
Description: Define all dependency and plugin versions in the <properties> section rather than hardcoding them throughout the POM. This centralizes version management, makes updates easier, reduces duplication, and helps maintain consistency across related dependencies. Use consistent property naming conventions: maven-plugin-[name].version for Maven plugins, simple names like [library].version for dependencies, and descriptive names for quality thresholds like coverage.level.
Good example:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<properties>
<!-- Core build properties -->
<java.version>17</java.version>
<maven.version>3.9.10</maven.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Dependency versions -->
<jackson.version>2.15.3</jackson.version>
<junit.version>5.10.1</junit.version>
<mockito.version>5.7.0</mockito.version>
<logback.version>1.4.11</logback.version>
<!-- Maven plugin versions -->
<maven-plugin-compiler.version>3.14.0</maven-plugin-compiler.version>
<maven-plugin-surefire.version>3.5.3</maven-plugin-surefire.version>
<maven-plugin-failsafe.version>3.5.3</maven-plugin-failsafe.version>
<maven-plugin-enforcer.version>3.5.0</maven-plugin-enforcer.version>
<!-- Third-party plugin versions -->
<maven-plugin-jacoco.version>0.8.13</maven-plugin-jacoco.version>
<!-- Quality thresholds -->
<coverage.level>80</coverage.level>
<mutation.level>70</mutation.level>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-plugin-compiler.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-plugin-surefire.version}</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${maven-plugin-jacoco.version}</version>
</plugin>
</plugins>
</build>
</project>
Bad example:
<!-- Hardcoded versions scattered throughout the POM -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.3</version> <!-- Hardcoded version -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.2</version> <!-- Different version of same library family! -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version> <!-- Hardcoded version -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.3</version> <!-- Different JUnit version! -->
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version> <!-- Hardcoded plugin version -->
<configuration>
<source>17</source> <!-- Hardcoded Java version instead of using property -->
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.2</version> <!-- Hardcoded plugin version -->
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version> <!-- Hardcoded and potentially outdated -->
</plugin>
</plugins>
</build>
</project>
Title: Organize a Multi-Module Build with a Root Aggregator POM and Proper Inheritance
Description: In a multi-module project the root POM acts as both aggregator (via <modules>) and parent (via inheritance). All shared dependency versions and plugin configurations belong in the root POM's <dependencyManagement> and <pluginManagement> sections. Child module POMs declare <parent> and reference managed artifacts without specifying versions. A dedicated BOM module can be introduced to decouple version management from build orchestration in large projects.
Good example:
<!-- Root aggregator / parent POM -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- Declare all child modules -->
<modules>
<module>my-api</module>
<module>my-service</module>
<module>my-web</module>
</modules>
<properties>
<java.version>21</java.version>
<jackson.version>2.17.0</jackson.version>
<junit.version>5.11.0</junit.version>
<maven-plugin-compiler.version>3.14.0</maven-plugin-compiler.version>
<maven-plugin-surefire.version>3.5.3</maven-plugin-surefire.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Cross-module dependency managed here -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-plugin-compiler.version}</version>
<configuration>
<release>${java.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-plugin-surefire.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
<!-- Child module POM (my-service/pom.xml) -->
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<!-- relativePath tells Maven where to find the parent without a repository lookup -->
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>my-service</artifactId>
<!-- No <groupId> or <version> — inherited from parent -->
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-api</artifactId>
<!-- Version managed centrally in root dependencyManagement -->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!-- Version managed centrally in root dependencyManagement -->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<!-- Version and scope managed centrally -->
</dependency>
</dependencies>
</project>
Bad example:
<!-- Root POM without <modules> declaration -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- Missing <modules> — child modules won't be built with the root -->
</project>
<!-- Child module POM (my-service/pom.xml) with redundant management -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId> <!-- Duplicated from parent -->
<artifactId>my-service</artifactId>
<version>1.0.0-SNAPSHOT</version> <!-- Not inherited — version drift risk -->
<parent>
<groupId>com.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<!-- Missing <relativePath> — forces Maven to look in local repository -->
</parent>
<!-- Redundant dependencyManagement duplicated across child modules -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version> <!-- Differs from sibling module's 2.17.0 — version drift! -->
</dependency>
</dependencies>
</dependencyManagement>
</project>
Title: Detect and Fix Version Drift Across Sibling Modules
Description: Version drift occurs when the same artifact is declared at different versions in sibling module POMs. This leads to non-reproducible builds and subtle runtime errors. The fix is to move all version declarations into the root POM's <dependencyManagement> (or a BOM module) and remove versions from every child POM. When performing multi-module analysis, read all sibling pom.xml files and compare declared dependency versions before recommending changes.
Good example:
<!-- Root POM: single source of truth for all versions -->
<project>
<!-- ... -->
<properties>
<logback.version>1.5.6</logback.version>
<slf4j.version>2.0.13</slf4j.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<!-- my-service/pom.xml — no version, inherits from root -->
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
<!-- my-web/pom.xml — no version, inherits from root (same version guaranteed) -->
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
Bad example:
<!-- my-service/pom.xml — hardcodes version -->
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version> <!-- Version A -->
</dependency>
</dependencies>
<!-- my-web/pom.xml — different hardcoded version for same artifact -->
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version> <!-- Version B — version drift! -->
</dependency>
</dependencies>
<!-- Result: Maven resolves each module independently.
Depending on classpath ordering, the wrong version may be used at runtime. -->
- DISCOVER the full project scope before analysis: read the root
pom.xmland check for a<modules>section; if present, read every child module'spom.xmlrecursively. List all discovered modules and their paths at the start of the response so the user knows the analysis covers the complete build - ANALYZE Maven POM files to identify specific best practices violations and categorize them by impact (CRITICAL, MAINTENANCE, PERFORMANCE, STRUCTURE) and area (dependency management, plugin configuration, project structure, repository management, version control)
- CATEGORIZE Maven configuration improvements found: Dependency Management Issues (missing dependencyManagement vs centralized version control, hardcoded versions vs property-based management, version conflicts vs resolution strategies, unused dependencies vs clean dependency trees), Plugin Configuration Problems (outdated versions vs current releases, missing configurations vs optimal settings, suboptimal configurations vs performance-tuned setups), Project Structure Opportunities (non-standard layouts vs Maven conventions, poor POM organization vs structured sections, missing properties vs centralized configuration)
- APPLY Maven best practices directly by implementing the most appropriate improvements for each identified issue: Introduce dependencyManagement sections for version centralization, extract version properties for consistency, configure essential plugins with optimal settings, organize POM sections following Maven conventions, add missing repository declarations, optimize dependency scopes, and eliminate unused dependencies through analysis
- IMPLEMENT comprehensive Maven configuration optimization using proven patterns: Establish centralized dependency management through BOMs or parent POMs, standardize plugin versions and configurations across modules, organize POM structure with clear sections (properties, dependencyManagement, dependencies, build), implement security best practices for repositories, apply dependency scope optimization, and integrate build lifecycle enhancements
- REFACTOR Maven configuration systematically following the improvement roadmap: First centralize version management through properties and dependencyManagement, then standardize plugin configurations with current versions, organize POM structure following Maven conventions, optimize dependency scopes and eliminate unused dependencies, secure repository configurations, and enhance build profiles for different environments
- EXPLAIN the applied Maven improvements and their benefits: Build reliability enhancements through centralized dependency management, maintenance simplification via property-based versioning, performance improvements from optimized plugin configurations, security strengthening through proper repository management, and development productivity gains from standardized build practices
- VALIDATE that all applied Maven changes compile successfully, maintain existing build behavior, preserve dependency compatibility, follow Maven best practices, and achieve the intended build improvements through comprehensive testing and verification
- MANDATORY: Analyze existing POM configuration before making any changes
- NEVER remove or replace existing plugins - only add new plugins that don't already exist
- NEVER remove or replace existing properties - only add new properties that don't conflict
- ASK USER before overriding any existing configuration element
- Verify changes with the command:
./mvnw clean verify - Preserve existing dependency versions unless explicitly requested to update
- Maintain backward compatibility with existing build process
- MULTI-MODULE SCOPE: When root POM contains
<modules>, always read and analyze ALL child module POMs before making any recommendations — never base advice on the root POM alone - VALIDATE ALL MODULES: After any change to the root or a child POM in a multi-module project, run
./mvnw clean verifyfrom the project root to confirm the full reactor build still passes - PRESERVE MODULE OVERRIDES: Some child modules intentionally override parent-managed versions or plugin configurations for valid reasons (e.g., a module requiring a different Java release). Before removing an override from a child POM, confirm with the user that the override is unintentional