How does Spring Boot keep all the libraries a starter pulls version-aligned? Explain the role of spring-boot-starter-parent vs importing the BOM.
answer
- spring-boot-dependencies = the BOM
- dependencyManagement pins versions, adds no jars
- parent POM vs import scope=import type=pom
- Override via version property
- Explicit version beats BOM
basics
~20 sVersions live in the spring-boot-dependencies BOM — a dependencyManagement list of managed versions. You inherit it either by using spring-boot-starter-parent as your Maven parent, or by importing spring-boot-dependencies directly (Gradle's Boot plugin does this for you). Starters then omit versions.
solid answer
~40 sThe alignment comes from a **Bill of Materials (BOM)** named `spring-boot-dependencies` — a POM with a large `<dependencyManagement>` block that pins a compatible version for every library in the ecosystem. Starters declare their transitive deps **without versions**; resolution fills them from the BOM, so every starter agrees on shared libraries. You consume the BOM two ways: (1) **`spring-boot-starter-parent`** — set it as your `<parent>`; it inherits from `spring-boot-dependencies` and adds plugin config, resource filtering, and Java defaults. (2) **BOM import** — when you already have a corporate parent, add `spring-boot-dependencies` to your own `<dependencyManagement>` with `<scope>import</scope>` and `<type>pom</type>`. Gradle uses the Spring Boot plugin (which applies dependency management importing the same BOM) or `platform(...)`. To override a version, redeclare the managed property (e.g. `<jackson.version>`); the BOM exposes such properties.
code
xml · 26 lines<!-- Option B: keep a corporate parent, import Boot's BOM for versions -->
<parent>
<groupId>com.acme</groupId>
<artifactId>acme-corp-parent</artifactId>
<version>9.2.0</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.4.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- no <version> needed: resolved from the imported BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>go deeper
Know that versions come from a BOM so you don't type them.
Explain spring-boot-dependencies BOM, parent vs import-scope, and how starters omit versions.
Discuss overriding managed versions safely, corporate-parent scenarios, and Gradle's platform/plugin equivalents.
Govern multi-BOM composition (Boot + Cloud), enforce alignment across many services, and weigh the risk of local version overrides against the tested matrix.
## Where versions actually come from A starter like `spring-boot-starter-web` lists dependencies (spring-webmvc, tomcat-embed, jackson-databind…) but typically **omits their `<version>`**. Something has to supply those versions. That something is **dependency management** driven by the **`spring-boot-dependencies` BOM**. ### What a BOM is A **Bill of Materials** is a POM used purely for its `<dependencyManagement>` section. It does not add dependencies to your build; it **declares versions** that take effect *if and when* a dependency is actually pulled. `spring-boot-dependencies` pins hundreds of coordinates (Spring, Hibernate, Jackson, Tomcat, Logback, testcontainers, etc.) at versions Spring's team tested together. ## Two ways to consume it (Maven) ### 1. Parent POM ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.1</version> </parent> ``` `spring-boot-starter-parent` **itself inherits from** `spring-boot-dependencies`, so you get all managed versions **plus** extras: sensible plugin management (the Boot Maven plugin, surefire), UTF-8/Java version defaults, resource filtering of `application.properties`, and property-driven overrides. ### 2. Import scope (when you already have a parent) Many companies have a mandated corporate parent POM, and Maven allows only one `<parent>`. Import the BOM instead: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.4.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` This gives version management **without** the parent's plugin/defaults conveniences (you configure those yourself). ## Gradle Gradle has no POM inheritance. The **Spring Boot Gradle plugin** (`org.springframework.boot`) together with `io.spring.dependency-management`, or Gradle's native `platform()`/`enforcedPlatform()`, imports the same BOM: ```kotlin plugins { id("org.springframework.boot") version "3.4.1" } // Boot plugin imports the spring-boot-dependencies BOM automatically, // so implementation("...:spring-boot-starter-web") needs no version. ``` ## Overriding a managed version Because the BOM exposes version **properties**, you override by redefining the property (with the parent): ```xml <properties> <jackson-bom.version>2.17.2</jackson-bom.version> </properties> ``` In Gradle: `extra["jackson.version"] = "2.17.2"` (via the dependency-management plugin) or a `resolutionStrategy`. **Gotcha:** overriding one library's version can break the tested compatibility matrix — do it deliberately and test. ## Key gotchas - The BOM only sets versions for coordinates you actually depend on; it doesn't add jars. - `import` scope pulls **only** dependencyManagement, never the parent's plugin config. - If you declare an explicit `<version>` on a dependency, it **wins** over the BOM (and silently escapes the alignment guarantee). - Mixing two BOMs (e.g., Spring Cloud + Spring Boot) requires importing them in the right order; nearest/first declaration wins.
- When would you import the BOM instead of using spring-boot-starter-parent?When your project already has a mandatory parent POM (e.g., a corporate parent). Maven allows only one <parent>, so you import spring-boot-dependencies with scope=import to still get version alignment, and configure Boot's Maven plugin yourself.
- How do you override the version of a single library managed by the BOM, and what's the risk?Redefine its version property (e.g. <jackson-bom.version> with the parent, or the equivalent in Gradle's dependency-management). Risk: you leave Spring's tested compatibility matrix, so an incompatible combination can cause subtle runtime/classpath failures — override deliberately and test.
saying these in an interview costs you the question
- Saying the BOM adds dependencies to the build (it only manages versions)
- Claiming import scope gives you the parent's plugin/resource-filtering conveniences
- Not knowing an explicit <version> overrides the BOM and silently breaks alignment