skip to content

When centralizing versions, what is the difference between using Maven <properties> for versions versus <dependencyManagement>, and how do they interact in a multi-module project?

level: seniorimportance: should knowfreq 40%

answer

  1. properties = text variable
  2. dependencyManagement = managed default
  3. combine: property inside DM
  4. child can override property
  5. BOM override via local property

basics

~20 s

A <properties> entry like <guava.version>33.2.1</guava.version> is just a reusable string you reference with ${guava.version}; you still write the version everywhere. <dependencyManagement> sets a managed default so child modules can omit the version entirely. Teams often combine both.

solid answer

~40 s

They operate at different layers. A `<properties>` value is a text variable: you define `<x.version>` once and substitute `${x.version}` wherever you need it — but each dependency still carries an explicit <version> tag. `<dependencyManagement>` is a resolution mechanism: it declares managed defaults so child `<dependencies>` can omit <version> and inherit it, and it also overrides transitive versions. The common idiom combines them: put the literal version in a property and reference it from the parent's dependencyManagement, giving you one obvious place to edit while children stay version-free. Properties are inherited by child POMs too, and a child can override a property to locally change a version. BOM import is really dependencyManagement behavior. Choose properties for readability/reuse (including plugin versions and arbitrary config), and dependencyManagement for actually governing what versions resolve.

code

xml · 12 lines
xml
<properties>
  <guava.version>33.2.1-jre</guava.version>
</properties>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>${guava.version}</version>
    </dependency>
  </dependencies>
</dependencyManagement>

go deeper

for a junior

Knows ${x.version} is a reusable variable and that DM lets children omit versions.

for a middle

Can write the property-inside-dependencyManagement idiom correctly.

for a senior

Explains the layering, inheritance/override rules, and when each is appropriate.

for a principal

Designs the parent-POM/BOM/property convention org-wide for clean upgrades and tooling (Dependabot) friendliness.

## Two different layers - **<properties>**: a simple key→string map. `<guava.version>33.2.1-jre</guava.version>` defines a variable; `${guava.version}` substitutes it during POM interpolation. It does nothing Maven-specific about dependencies — it's just text reuse, usable for plugin versions, config values, anything. - **<dependencyManagement>**: a Maven resolution feature. It registers managed defaults (version/scope/exclusions) so a child <dependencies> entry can omit the version and inherit it, and it overrides transitive versions during mediation. ## They are complementary, not exclusive A very common parent-POM pattern: ```xml <properties> <guava.version>33.2.1-jre</guava.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>${guava.version}</version> </dependency> </dependencies> </dependencyManagement> ``` Now: - The literal version lives in ONE property (easy to scan/bump, friendly to Dependabot). - Child modules just declare guava with no version and inherit it. ## Inheritance and overrides - **Properties are inherited** by child POMs and can be **overridden** in a child to change the value locally. - **dependencyManagement is inherited** too; a child's own dependencyManagement or a direct <version> overrides the inherited managed version. - Imported BOMs are inlined into your dependencyManagement, and your local management (often property-driven) overrides BOM versions — a frequent way to bump one library above what a BOM ships. ## When to use which - Use **properties** when you want a named, reusable value — especially plugin versions (there's no automatic version inheritance for plugin <dependencies> the way there is for managed deps) and shared config. - Use **dependencyManagement** when you want children to drop the <version> and to govern transitive versions. - Use **both** for the cleanest multi-module setup. ## Pitfall A property alone does NOT let a child omit <version> — you still must write `<version>${x.version}</version>` on each dependency. Only dependencyManagement gives true version-free child declarations.

  • Does defining <guava.version> as a property let child modules omit <version>?
    No. A property is just a variable; each dependency still needs <version>${guava.version}</version>. Only dependencyManagement enables version-free child declarations.
  • How do you bump a single library above what an imported BOM provides?
    Declare that artifact in your own <dependencyManagement> (often via a property) — local management overrides the imported BOM.
  • Can a child module override a version property from the parent?
    Yes — properties are inherited and a child can redefine the property to change the value for that module.

saying these in an interview costs you the question

  • Claiming a <properties> entry alone lets children skip <version>.
  • Treating properties and dependencyManagement as interchangeable.
  • Saying you cannot override a BOM version (you can, via local dependencyManagement).

context