Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

For a reliable multi-module Maven quality check, configure shared plugin versions in the parent POM, activate the checks you want under <build><plugins>, and bind enforcement goals to the build lifecycle. Run mvn clean verify from the reactor root for the complete build. Treat aggregate coverage as a separate configuration problem: a dedicated report module that depends on the modules being measured avoids relying on the root aggregator to run after its children.

Decide what the build should check

“Code quality analysis” can mean several distinct checks. Choose tools for the findings your team will review, and keep report generation separate from build enforcement.

Tool What it checks Typical build role
Checkstyle Source formatting and coding conventions Report style violations; the check goal can fail the build.
PMD Rule-based source analysis Generate findings and use check to enforce them. CPD is a separate, optional duplication check.
SpotBugs Potential defects identified by analyzing compiled bytecode Run after compilation; use its check goal to enforce configured findings.
JaCoCo Test coverage data Instrument test execution and produce per-module or aggregate coverage reports; configure a check separately if coverage should gate the build.

These tools identify particular classes of findings; none proves that a project is defect-free or provides a single, complete measure of quality. Begin by deciding whether violations should initially be reports only, warnings, or build failures. Avoid imposing a universal coverage percentage: a useful target depends on project risk, test design, and whether the team measures total code or changed code.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Confirm the reactor and parent relationship

A common layout has a root POM, Java modules, and optionally a dedicated reporting module:

parent/
  pom.xml
  module-a/pom.xml
  module-b/pom.xml
  quality-report/pom.xml

The root POM commonly has <packaging>pom</packaging> and lists the projects in <modules>. Listing a project makes it part of the reactor, but does not make it inherit the root’s build configuration. Each child that should inherit shared checks must also reference the intended parent in its <parent> element. Aggregation and inheritance are separate POM functions; see the Maven POM reference.

The reactor collects projects and sorts them into build order. It considers instantiated project dependencies and plugin or extension relationships, with module declaration order used when no other sorting rule applies. Entries in dependencyManagement and pluginManagement alone do not establish reactor ordering. The Maven multi-module guide describes reactor behavior and project selection.

Decide which modules are in scope. A POM-packaged parent or a documentation module may have no Java sources or bytecode to analyze. Exclude or configure such modules intentionally rather than allowing an unexplained “no sources” failure. Modules that use a different parent may also need their own plugin configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Understand plugin management, activation, and lifecycle binding

Put common plugin versions and defaults in the parent’s <build><pluginManagement>. That section manages configuration; by itself, it does not activate a plugin or make its goals run. Declare the plugins and executions the build should actually use under <build><plugins>. Maven’s plugin configuration guide explains the distinction.

<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <version>PIN_A_VERIFIED_RELEASE</version>
      </plugin>
      <!-- Manage PMD, SpotBugs, and JaCoCo versions here too. -->
    </plugins>
  </pluginManagement>

  <plugins>
    <!-- Declare plugins and lifecycle-bound executions to activate. -->
  </plugins>
</build>

In a parent POM, put activated plugins and shared executions in <build><plugins> when child modules should inherit them. A child can alter or override inherited configuration, so inspect the effective POM or Maven logs if behavior differs between modules. The <reporting> section is for Maven Site reports; it is not a substitute for lifecycle-bound build enforcement.

For CI, bind enforcement goals explicitly to verify if that is the team’s chosen gate, then run the lifecycle from the reactor root. Bytecode analysis must happen after compilation, and coverage reporting must follow test execution. A generated HTML or XML report is not automatically a build gate: the goal and its failure or threshold settings determine whether Maven fails.

Configure the analyzers and shared rules

Pin released plugin versions in the project and check the selected releases’ requirements against the project’s Maven and Java versions. Documentation checked on September 24, 2026 lists Checkstyle plugin version 3.6.0 in its multi-module example, SpotBugs Maven plugin version 4.10.4.1, and JaCoCo 0.8.16 as its current release; the JaCoCo Maven page also displays a snapshot version. These are dated references, not a promise of compatibility with every project. For PMD, use the current release documentation for the Apache Maven PMD Plugin: the PMD user guide and plugin documentation have conflicting version statements. See the Checkstyle multi-module example, SpotBugs goal reference, JaCoCo Maven documentation, and Apache Maven PMD Plugin documentation for current plugin details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Checkstyle: share style rules and decide on enforcement

Keep the Checkstyle rules in a version-controlled configuration and make every module resolve the same resource. Relative paths can resolve differently from child-module directories. Apache’s multi-module configuration example shows a build-tools module that supplies the configuration as a plugin dependency; it also notes that plugin dependencies do not work inside <reporting>. Inline configuration in the parent POM is another option supported by the example. Avoid duplicating plugin declarations in children unless you intend to override the inherited setup.

The Checkstyle check goal reports violations and can fail the build; its documented failOnViolation default is true. Check the selected plugin documentation before relying on defaults, and consider whether existing findings require a staged rollout. The plugin also offers checkstyle-aggregate for an aggregate HTML report. A report goal and an enforcement goal serve different purposes; see the Checkstyle check goal and plugin goal list.

PMD: run rules and treat duplication separately

PMD’s usage guide documents mvn compile pmd:pmd as a manual way to generate a report. Its check goal can be bound into the build and is documented to run during verify; the documented failOnViolation default is true. Consult the selected plugin version’s goal documentation when setting or changing behavior: PMD Maven usage and the PMD check goal.

PMD’s CPD goals perform copy-and-paste detection separately from PMD’s rule checks. Add them only if duplicated code is a finding your team will triage; CPD does not replace rule-based analysis.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

SpotBugs: analyze compiled classes

SpotBugs analyzes bytecode, so run it after the relevant modules have compiled. Its Maven plugin supplies the spotbugs:check goal for enforcement. The SpotBugs FAQ describes aggregate reporting with spotbugs:spotbugs-aggregate after per-module spotbugs:spotbugs results have been generated; an aggregate report should not be assumed to be an aggregate enforcement check. Follow the SpotBugs Maven usage guide and SpotBugs FAQ for the selected configuration.

SpotBugs runs in a separate process. If a large project encounters memory pressure, identify whether that process or Maven itself is constrained before adjusting heap settings; the plugin FAQ documents the maxHeap option.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Generate JaCoCo coverage, including one combined report

JaCoCo provides prepare-agent, report, report-aggregate, and check goals. A typical setup attaches the agent to test execution, generates per-module reports, and optionally uses check to enforce a deliberately chosen coverage rule. Consult the JaCoCo Maven plugin documentation for the relevant goal configuration.

For test coverage to be recorded, Surefire or Failsafe must not run with forkCount=0 or the legacy forkMode=never: in that configuration the test JVM does not receive JaCoCo’s configured Java agent. Confirm that tests run and that execution data is created. Source highlighting and line information in reports also require compilation with debug information.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If you want one combined report, use a dedicated reporting module that depends on the modules whose execution data and classes should be included, then run JaCoCo’s aggregate goal after those modules have produced coverage data. Do not assume a goal bound to the root aggregator will run after its children: Maven builds aggregator projects before their submodules. JaCoCo’s multi-module guidance explains the report-module pattern and timing issue. If you use Maven Site, configure JaCoCo reportSets explicitly to select the intended goal and avoid redundant aggregate reports.

Run the complete build and local subsets

Use the full reactor command as the authoritative local or CI check:

mvn clean verify

Maven builds the selected reactor projects, runs lifecycle-bound tests and checks, and returns a failure if a configured check rejects a module. This command only generates the reports bound or otherwise configured to run in that build; aggregate coverage may require the report module or an additional goal/configuration.

For faster local iteration, Maven can select a module and include its required reactor dependencies, or continue through the reactor after a failure:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
mvn -pl module-a -am verify
mvn --fail-at-end clean verify

-pl selects projects, -am also makes required reactor dependencies, and --fail-at-end continues building other modules before reporting failures. A partial build may omit affected dependents or the aggregate report, so use the full root build when validating cross-module behavior. See the Maven reactor guide.

Troubleshoot missing checks, reports, and noisy findings

  • No analysis runs: Check that the plugin is declared under active <build><plugins> and that an execution or command invokes the intended goal. A version in pluginManagement alone is not activation.
  • Only some modules are checked: Confirm those children inherit the expected parent and have not overridden the plugin. A module listed under <modules> but using another parent does not automatically inherit the parent’s build plugins.
  • Shared Checkstyle rules are missing: Verify how the configuration resource is resolved from each child. Use a tested classpath-resource approach, such as the documented build-tools plugin dependency pattern, or inline configuration where supported.
  • Aggregate report is absent or incomplete: Ensure every intended module is included in the reporting module’s dependencies and that module outputs exist before aggregation. Do not rely on the root aggregator executing after its children.
  • Coverage is empty despite passing tests: Check that tests actually ran, inspect JaCoCo execution data, and confirm Surefire or Failsafe is not configured with forkCount=0 or forkMode=never.
  • Analysis fails in a module with no sources or classes: Decide whether the module belongs in the analysis scope; parent POMs and non-Java modules may need an intentional exclusion or suitable configuration.
  • Findings include generated code or language-level parse errors: Match analyzer language settings to the module’s Java version, decide whether generated sources are in scope, and use exclusions that match their actual paths. Checkstyle exposes excludeGeneratedSources for this purpose.
  • SpotBugs runs out of memory: Identify which process is constrained, then consider its documented maxHeap option or the Maven JVM’s memory settings as appropriate.
  • Existing findings block adoption: Review default rules, publish reports first if needed, and establish a baseline or ratchet checks into a controlled scope. Keep suppressions narrow, documented, and subject to review.

Keep checks maintainable

Pin analyzer versions and keep common rules and suppressions under version control. When upgrading an analyzer or rule set, review release notes and handle changed findings as a deliberate build change. Choose a set of checks the team can triage consistently: overlapping tools can increase build time and maintenance without producing useful action. For each check, make clear whether it only publishes a report or can fail the build, and keep module scope, language settings, and exclusions explicit.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.