This is how Spring Boot and large projects keep hundreds of dependency versions consistent.
<dependencyManagement> declares versions (and scopes, exclusions) without adding the dependencies. Modules or the <dependencies> section then list only groupId and artifactId, inheriting the managed version. Managed versions also apply to transitive dependencies, so declaring jackson-databind there pins the version wherever it appears in the graph.
A BOM (bill of materials) is a POM with <packaging>pom</packaging> containing only a dependencyManagement section. Import it with scope import:
<dependencyManagement><dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.4.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies></dependencyManagement>After that, Spring, Jackson, JUnit and dozens of other libraries can be declared without versions and are guaranteed to be mutually compatible. Spring Boot projects more often inherit from spring-boot-starter-parent, which imports this BOM and also configures plugins; importing the BOM is the alternative when you need your own parent.
Precedence: a version declared directly in <dependencies> beats a managed one; among managed versions, the first declaration in the POM (including the order of imported BOMs) wins. Use mvn help:effective-pom to see the final result and mvn dependency:tree -Dverbose to see which versions were managed.
Example
<dependencyManagement>
<dependencies>
<dependency> <!-- import a BOM -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.4.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency> <!-- pin a version everywhere -->
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.4.0-jre</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency> <!-- versions come from management -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>Remember
- dependencyManagement sets versions without adding dependencies
- Managed versions also override transitive versions
- Import a BOM with type pom and scope import
- Direct versions beat managed ones; first managed declaration wins