| name | maven |
| description | [Applies to: **/*.java] This guide provides opinionated, actionable best practices for Maven projects, focusing on reproducible builds, efficient dependency management, and high code quality. |
| source | cursor_mdc |
Maven Best Practices
Maven is the definitive build tool for modern Java. Adhere to these rules for consistent, maintainable, and high-quality projects.
Code Organization and Structure
Centralize Configuration with a Parent POM/BOM
Always use a parent POM (or Bill of Materials - BOM) to define shared properties, plugin versions, and a <dependencyManagement> section. This guarantees consistent library versions and build behavior across all modules.
❌ BAD: Duplicating dependency versions and plugin configurations in every child pom.xml.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration><release>17</release></configuration>
</plugin>
</plugins>
</build>
✅ GOOD: Define a parent POM with <dependencyManagement> and <pluginManagement>. Child modules inherit these.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>my-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.2.0</spring-boot.version>
<maven.compiler.plugin.version>3.11.0</maven.compiler.plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
pom
import
org.apache.maven.plugins
maven-compiler-plugin
${maven.compiler.plugin.version}
${java.version}
com.mycompany
my-parent
1.0.0-SNAPSHOT
4.0.0
my-child-module
org.springframework.boot
spring-boot-starter-web
Common Patterns and Anti-patterns
Declare the Narrowest Possible Dependency Scope
Limit dependency transitivity and classpath bloat by using the most restrictive scope (compile, provided, runtime, test).
❌ BAD: Using compile scope for everything, including test-only dependencies.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version>
</dependency>
✅ GOOD: Explicitly define scopes.
compile (default): Available in all classpaths, propagated to dependents. For core libraries.
provided: Expected to be provided by JDK or container at runtime. For Servlet API, Java EE APIs.
runtime: Not needed for compilation, but for execution. For JDBC drivers.
test: Only for test compilation and execution. For JUnit, Mockito.
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Manage Transitive Dependencies with Exclusions
Prevent unwanted or conflicting transitive dependencies from polluting your classpath.
❌ BAD: Relying on Maven's "nearest definition" for conflicting transitive dependencies, leading to unpredictable builds or runtime issues.
<dependency>
<groupId>com.example</groupId>
<artifactId>library-b</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>library-c</artifactId>
<version>1.0</version>
</dependency>
✅ GOOD: Explicitly exclude problematic transitive dependencies and declare the desired version directly.
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>library-b</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.example</groupId>
<artifactId>dependency-d</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>library-c</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>dependency-d</>
2.0
Performance Considerations
Use Maven Wrapper for Consistent Builds
Ensure all developers and CI/CD pipelines use the exact same Maven version, preventing "works on my machine" issues related to Maven itself.
✅ GOOD: Include Maven Wrapper in your project.
mvn wrapper:wrapper
Then, use ./mvnw (or mvnw.cmd on Windows) instead of mvn.
./mvnw clean install
Common Pitfalls and Gotchas
Avoid Over-reliance on Maven Profiles for Environment Configuration
Modern applications should be environment-agnostic (12-factor app principles). Use external configuration (environment variables, configuration servers) for environment-specific settings, not Maven profiles.
❌ BAD: Using profiles to inject environment-specific database URLs or API keys.
<profiles>
<profile>
<id>dev</id>
<properties>
<db.url>jdbc:h2:mem:devdb</db.url>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<db.url>jdbc:postgresql://prod-db:5432/appdb</db.url>
</properties>
</profile>
</profiles>
✅ GOOD: Keep application configuration external to the build. Maven profiles are acceptable for build process variations (e.g., different test suites, packaging types), but not runtime environment configuration.
Testing Approaches
Integrate Static Analysis Plugins
Enforce coding standards and identify potential issues early by integrating static analysis tools like Checkstyle, SpotBugs, or PMD into the Maven build lifecycle (e.g., verify phase).
✅ GOOD: Configure static analysis plugins in your parent POM's <build> section.
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<failsOnError>true</failsOnError>
</configuration>
</execution>
com.github.spotbugs
spotbugs-maven-plugin
4.8.3.0
analyze
verify
check