One of the biggest advantages of Maven is that it makes things easiest when best practices are followed. There are several recommended steps to take in setting up your build and developing the project that will help detailed here.
Many have complained that Maven forces a certain structure on a project. While this is not completely true (most settings are still configurable), it certainly does make it easier to use defaults.
From the layout of your source code to the layout of your project documentation, and all the standard goals in between, following conventions helps new users find their way around your project with minimal training and additional documentation.
For more information, refer to the Conventions Reference.
It is important to make sure a build is reproducible. This means a few things:
Making sure any user can check it out and build means that you must avoid the need for properties specific to your environment. The best practices to follow here are:
$HOME/build.properties
and ensure they are only
those required for your particular environment, and that it is well known that they need to be set (eg
HTTP proxy settings, J2EE server installation path)build.properties
in the project,
but don't commit it to source control. Commit a sample properties file that other developers will know to
copy and edit accordingly. However, wherever possible, use sensible defaults to avoid the need for this.Being able to check out a historical codebase is more important in some environments than others, but often you don't know you need it until it is too late, so it is a good practice to follow.
maven --info
. Future versions of Maven aim to make this easier to keep consistent
through the life of the project.Finally, make sure generated artifacts are always built identically. This is important as two files with the same name, but different settings built in can become very confusing. Also, you often do not want to have to rebuild an artifact as it goes from staging to QA to production as it introduces the risk that it may be built differently (especially if the previous principles were not adhered to!) Some recommendations for this are:
One helpful method to assist reproducibility, if your environment has the capacity, is to set up a user on a machine with a clean environment that matches the target environment, with known properties, plugins and other software. This user can be used to build releases from a known baseline.
Try to minimise the amount of scripting done. Those familiar with Ant will often put a lot of Ant tasks into
maven.xml
. You should try not to have a maven.xml
file.
The reason for this is that this starts to make each build behave differently, losing one of the benefits of having a uniform environment. It also requires more maintenance and documentation.
Try to look for plugins that provide the functionality you are looking for, configure existing plugins, or even make small rearrangements to your project if you are just looking to reproduce existing functionality in a different way.
If you do have to do scripting, it is highly recommended to create a separate plugin project that provides these services so that it does not need to be duplicated across projects and the changes are isolated.
Things that are often placed in maven.xml
are shortcut names for commonly used goal combinations,
specification of the default goal, and pre/postGoal definitions to make small modifications to the behaviour
of an existing goal - for example to add a new source root before compilation.
Writing plugins is the best way to extend Maven and continue to share it among other projects. However due to its architecture there are some things that are not recommended.
Firstly, try not to use pre/postGoal
definitions inside a plugin. This will bind that plugin to
another plugin causing side effects when the plugin is loaded, but different effects when it is not. A better
solution is to define the behaviour in a new goal, and then have the projects using the plugin define the
pre/postGoal
to call the new goal.
Also, try not to call into other plugins using attainGoal
. This makes your plugin susceptible to
changes in the other plugin. Often you won't have an option - however the recommended way for plugins to
expose their functionality is using Jelly Tag Libraries, or even better - shared Java code.
Use of prereqs
is strongly preferred over attainGoal
in any situation other than
inside pre/postGoal
definitions. This ensures the best order can be determined and that goals
are only executed once.
Whenever you start making changes after a recent release, you should check the currentVersion
tag in project.xml
and ensure that it is nextVersion-SNAPSHOT
.
By tagging the version as -SNAPSHOT
it signifies it is not released (so won't accidentally
overwrite the official release), and has the advantage that when published, newer versions will be downloaded
if it changes.
Part of the release process is to set currentVersion
to the actual version released.
For more information, set Making Releases.
The
xdocs
directory should contain a set of documentation that can be published as a site,
giving users and developers instructions on how to use the project.
When you make changes to the project, make sure the documentation is kept up to date.
For plugins, consider especially the
xdocs/goals.xml
and
xdocs/properties.xml
files. If the plugin has no
xdocs, you can generate skeletons using
maven plugin:generate-docs
.
The
xdocs/changes.xml
file can be used to track changes as they are made, including who made them,
what ticket number they relate to, and who they were contributed by if it was submitted as a patch.
Maintaining this gives a more readable change log than that provided by the SCM reports, and can be used to generate documentation and release notes.
When committing a change, you should edit and commit
xdocs/changes.xml
(create it if it does not
exist) and describe the change according to the format given in the
changes plugin.
Note: In the future, Maven should be able to integrate tightly with your issue tracking system to reduce the need for this.
You will find that it is much easier to work with Maven when projects are simple and independent. This kind of loosely coupled structuring is often recommended for source code, so it makes sense to apply the same thought process to the structure of your build.
Don't be afraid to split an artifact in two if it is becoming complicated to maintain the build.
Some specific examples:
Dependency Scopes: Maven 2 has a concept of scopes which are used to control when and where the dependencies are used in terms of e.g. the compile, test and runtime class paths. Maven 1 doesn't have this concept but it's still useful to include this information when adding dependencies to your project. It will be useful when you (possibly) convert your project to Maven 2 and if your repository is ever converted to a Maven 2 repository the Maven 2 POMs will include dependencies with the correct scope.
To set the scope on your dependency use the scope tag like this:
<dependency> <groupId>foo</groupId> <artifactId>bar</artifactId> <version>1.0</version> <properties> <scope>test</scope> </properties> </dependency>