skip to content

Why should core plugin versions be pinned and managed centrally, and how do you do it across a multi-module build?

level: principalimportance: should knowfreq 35%

answer

  1. unpinned versions = Maven-distro-dependent = non-reproducible
  2. pluginManagement in parent POM
  3. pluginManagement declares, plugins binds
  4. enforcer requirePluginVersions
  5. outputTimestamp for reproducible jars

basics

~10 s

If you don't pin core plugin versions, Maven picks defaults that can change between Maven releases, making builds non-reproducible. Pin them in <pluginManagement> in a parent POM so every module uses the same versions.

solid answer

~40 s

Core plugins are inherited from the Super POM, but the *exact* version chosen depends on your Maven distribution unless you pin it. That makes builds non-reproducible and surfaces 'works on my machine' problems. The fix is `<pluginManagement>` in a parent/company POM that declares versions (and shared configuration) for maven-jar/resources/install/deploy/clean, etc. Child modules then reference the plugins (or inherit bindings) without repeating versions, and everyone gets identical behavior. This also enables supply-chain hygiene: you review and bump plugin versions deliberately, can enforce with maven-enforcer-plugin's requirePluginVersions rule, and avoid surprise behavior changes. For full reproducibility you also set `project.build.outputTimestamp` and use the reproducible-build settings, but pinning plugin versions is the foundational step.

code

xml · 11 lines
xml
<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.4.1</version>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

go deeper

for a junior

Know that plugins have versions and unpinned ones can vary.

for a middle

Use pluginManagement in a parent to set versions once.

for a senior

Explain reproducibility risk and the pluginManagement-vs-plugins distinction.

for a principal

Establish org-wide governance: parent POM, enforcer rules, version-bump process, CVE scanning, and reproducible-build settings.

## The problem: implicit plugin versions For a normal jar project you never declare maven-jar-plugin, yet it runs — it's inherited from the **Super POM** that every project extends. But which *version* runs is tied to your Maven installation's defaults. Two developers (or your laptop vs CI) on different Maven versions can get **different plugin versions**, hence different bytes or behavior. Builds become non-deterministic and hard to debug. ## The fix: pluginManagement in a parent POM `<pluginManagement>` declares versions and default configuration **without binding** the plugin — children that use the plugin (or inherit its default lifecycle binding) pick up the managed version. Put this in a company/parent POM that all modules inherit via `<parent>`. ```xml <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.4.1</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.3.1</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>3.1.2</version> </plugin> </plugins> </pluginManagement> </build> ``` ## pluginManagement vs plugins - `<pluginManagement>` = *declare* versions/config; does not activate the plugin by itself (except for plugins already bound by packaging defaults, which then adopt the managed version). - `<plugins>` = actually *use/bind* the plugin in this module. This mirrors `dependencyManagement` vs `dependencies`. ## Enforcing it The **maven-enforcer-plugin** rule `requirePluginVersions` fails the build if any plugin runs without an explicit version, catching drift. CI should run enforce. ## Reproducible builds Pinning versions is necessary but not sufficient for byte-identical jars. Add `<properties><project.build.outputTimestamp>...</project.build.outputTimestamp></properties>` so the resources/jar plugins use a fixed timestamp instead of the current time. Together they yield reproducible artifacts. ## Governance angle A single parent/BOM-style POM lets a platform team review and bump plugin versions deliberately, scan for CVEs in plugins, and roll changes out consistently — central control over the entire org's build behavior and supply chain.

  • What is the difference between pluginManagement and plugins?
    pluginManagement only declares versions/config (it doesn't activate the plugin on its own), while plugins actually binds/uses it. Children inherit the managed version when they use the plugin — analogous to dependencyManagement vs dependencies.
  • How can you enforce that no plugin runs without a pinned version?
    Use maven-enforcer-plugin with the requirePluginVersions rule, which fails the build if any executing plugin lacks an explicit version.
  • Besides pinning versions, what else is needed for reproducible jars?
    Set project.build.outputTimestamp so the jar/resources plugins use a fixed timestamp instead of the current build time, yielding byte-identical artifacts.

saying these in an interview costs you the question

  • Claiming inherited plugins always use a fixed version regardless of Maven distribution.
  • Thinking pluginManagement activates a plugin by itself.
  • Repeating versions in every module instead of centralizing in a parent.

context