Maven Best Practices
This page describes my personal best practices for Apache Maven.
Gradually learn Maven
Maven is a great tool but it has a steep learning curve. Therefore, have Maven: The Complete Reference in your bookmarks and refer to it frequently.
Know Maven's command-line options
To learn about Maven's command-line options, run
mvn -?. Useful options include:
-B- runs Maven in batch mode, which is particularly useful when invoking Maven from a continuous server such as Bamboo because it avoids Maven's reporting of downloading progress
-e- configures Maven to report detailed information about errors, which is particularly useful to debug problems that occur when Maven is invoked from a continuous server such as Atlassian Bamboo
Know the standard Maven plugins
To learn about a specific Maven plugin, browse the plugin's Maven site and read the details on each of the plugin's goals.
Prefer a one-to-one relationship between artifacts and Maven projects
For example, if your code base creates three JAR files, have three Maven projects. If all three JAR files share common code, use a fourth Maven project to store the common code. Have the original three projects define a dependency on the common code.
Name your project
Even though the
<name data-preserve-html-node="true" data-preserve-html-node="true"> element is optional in a
pom.xml, use it! Forge services (such as Sonar) use a project's name. In IDEs it is hard to distinguish between two unnamed projects.
Put all generated artifacts in the
target folder is automatically deleted when you run
mvn clean. Therefore, it's the best place to put all build artifacts, such as Java
.class files, JAR files, and temporary files created during the build process. Maven plugins typically place the temporary artifacts they create somewhere within the
dependencyManagement section in a parent
pom.xml to avoid duplicating dependency information in child projects
dependencyManagement section allows a parent
pom.xml to define dependencies that are potentially reused in child projects. This avoids duplication; without the
dependencyManagement section, each child project has to define its own dependency and duplicate the version, scope, and type of the dependency. For example, suppose a multi-module Java project makes extensive use of JUnit for unit testing. To specify the project dependency on JUnit, the project's parent
pom.xml can use a
dependencyManagement section as follows:
Dependency Management in parent
<dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement>
Note that each child project that uses JUnit must still specify its dependency on JUnit. However, the
scope of the dependency should be omitted as this avoids duplication and ensures that the same dependency is used throughout the project.
Dependency in child
pom.xml (version and scope inherited from parent)
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies>
SNAPSHOT versions during development
SNAPSHOT versions take some getting used to, but they provide significant benefits:
- They reduce the usage of Forge resources. For example, they reduce the amount of disk space used by Nexus.
- They reduce the frequency of changes that dependent projects make. For example, when project A depends on project B and both projects are under development, project A can declare a dependency on the
SNAPSHOTversion of project B. That way, project A doesn't need to change its dependency every time there is a new build of project B.
Use the Maven Dependency Plugin's analyze goals to help identify issues in your project's dependency management
The Maven Dependency Plugin has an
analyze goal that identifies two types of dependency issues in a project:
- Dependencies that are directly used but are not declared. (The project still compiles because it gets the dependencies transitively.)
- Dependencies that are declared but are unused.
You can configure a project's build to fail if it has any dependency warnings - refer to the Maven documentation.
Used undeclared dependencies
Suppose the following:
- Project A uses classes from projects B and C.
- Project A only declares a Maven dependency on project B.
- Project B uses classes from project C.
- Project B declares a Maven dependency on project C.
This situation is a lurking problem because project A is relying on project B for its dependency on project C. Project A will compile until project B removes its definition of its dependency on project C. This problem is made even worse when the dependency is on a
When project A is analyzed using
mvn dependency:analyze, Maven produces the following output:
[WARNING] Used undeclared dependencies found: [WARNING] com.avaya.ace.aaft:foundation_services_client_api:jar:6.2.0-SNAPSHOT:compile
To fix the problem, project A should define a direct Maven dependency on project C.
Unused declared dependencies
If a project declares a dependency and then does not use that dependency,
mvn dependency:analyze produces the following output:
[WARNING] Unused declared dependencies found: [WARNING] com.gigaspaces:gs-openspaces:jar:7.1.1-b4534:compile
To fix the problem, the project should remove the dependency from its POM.
Unused dependencies sometimes occur because developers are unaware of Maven's
Use Maven for dependency management rather than writing custom code for it
For example, if one Maven project depends on a zip file created by another Maven project, have the second project create a Maven artifact and use a Maven dependency rather than using a relative path.
Use Maven for dependency management rather than using Subversion externals
For example, if one Maven project depends on resources stored in another project's repository, have the second project create a Maven artifact. Use a Maven dependency to allow the first project to access the artifact.
Do not write custom Maven code to copy or unzip files.
Use Maven's standard plugins instead.