skip to content

What is <pluginManagement> and why pin plugin versions there instead of declaring them in <plugins>?

level: seniorimportance: must knowfreq 65%

answer

  1. like dependencyManagement, for plugins
  2. declares version + config, does NOT activate
  3. child lists groupId/artifactId, no version
  4. reproducible builds
  5. enforcer requirePluginVersions

basics

~20 s

<pluginManagement> declares plugin versions and default config in a parent POM without actually enabling the plugin. Child modules that list the plugin (by groupId/artifactId, no version) inherit the pinned version and config, giving consistent, reproducible builds.

solid answer

~40 s

`<pluginManagement>` (inside `<build>`) is the plugin analogue of `<dependencyManagement>`: it centralizes plugin **versions** and **default configuration** in a parent/aggregator POM but does **not** add the plugin to the build by itself. A module activates the plugin by listing it in `<build><plugins>` with just `groupId`/`artifactId` (no version); it then inherits the managed version and config and may override per module. Pinning versions there prevents Maven from silently resolving a plugin version (which can change between Maven releases or registry state), eliminating non-reproducible builds and the dreaded "works on my machine." It also lets you configure a plugin once for the whole reactor. The `maven-enforcer-plugin` `requirePluginVersions` rule can fail the build if any plugin lacks an explicit version, enforcing this discipline.

code

xml · 12 lines
xml
<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
        <configuration><release>17</release></configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

go deeper

for a junior

Knows it pins plugin versions in a parent POM.

for a middle

Knows it does not activate the plugin and that children must still declare it.

for a senior

Explains reproducibility motivation, config inheritance/merge, and enforcing pinned versions via enforcer.

for a principal

Designs the parent/BOM-style POM hierarchy and governs plugin versioning across many modules/teams.

## The problem it solves If you use a plugin without specifying a `<version>`, Maven must *resolve* one. Historically it picked the latest available, and even now the resolved version can differ across Maven distributions or environments. That makes builds **non-reproducible** — the same source can compile or package differently on two machines or two days apart. ## <pluginManagement> vs <plugins> They live in different sub-elements of `<build>`: - **`<plugins>`** — actually adds/activates a plugin in *this* module's build. - **`<pluginManagement>`** — *declares* version and default config for a plugin, but does **not** activate it. It only takes effect when a module (this one or a descendant) also lists the plugin in `<plugins>`. ```xml <!-- parent / aggregator POM --> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.5</version> <configuration> <argLine>-Xmx512m</argLine> </configuration> </plugin> </plugins> </pluginManagement> </build> ``` ```xml <!-- child module POM: activates with inherited version + config --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> </plugin> </plugins> </build> ``` ## Why this is the standard pattern - **Reproducibility** — every module uses the exact same plugin version. - **Single source of truth** — upgrade the version once in the parent. - **Consistent defaults** — shared `<configuration>` and even `<executions>` defined under management are inherited where the plugin is used. - **Cleaner child POMs** — modules list only `groupId`/`artifactId`. ## Enforcing it Add the `maven-enforcer-plugin` with the `requirePluginVersions` rule so the build fails if any plugin (including lifecycle-default ones) lacks an explicit version. This guarantees the discipline holds across the whole reactor. ## Subtlety: management config still merges Configuration declared in `<pluginManagement>` is merged with the activating `<plugins>` declaration using the normal Maven merge rules (with `combine.children`/`combine.self` available to control list merging) — so a module can add to or override the inherited defaults.

  • Does putting a plugin in <pluginManagement> make it run?
    No. pluginManagement only supplies version and default config. The plugin must also be declared in <build><plugins> (in this module or a descendant) to actually participate in the build.
  • How can you fail the build when a plugin version is unpinned?
    Use maven-enforcer-plugin with the requirePluginVersions rule; it errors if any plugin, including lifecycle defaults, has no explicit version.
  • How does <pluginManagement> relate to <dependencyManagement>?
    Same idea on the plugin side: both centralize versions/config in a parent without activating anything, and both are activated by a matching, version-less declaration in the child.

saying these in an interview costs you the question

  • Saying pluginManagement activates/runs the plugin
  • Thinking it is only for versions and never config
  • Letting plugins run with unpinned versions ('latest')
  • Confusing it with <dependencyManagement> as if they were the same element

context