As a tech lead across many services, how would you govern dependency-version overrides so teams can patch CVEs without silently drifting off Spring Boot's tested baseline?
answer
- Overrides = tracked debt against a shared baseline
- Company platform/BOM centralizes + makes visible
- Justify (CVE/ticket) + surgical + no Spring downgrade
- CI: convergence + vuln scan + integration tests
- Prefer upgrading Boot; retire overrides each upgrade
basics
~20 sStandardize how versions are managed (shared parent/platform), require every override to be justified, minimal, and time-boxed, verify with dependency-tree checks and tests in CI, and review overrides each Boot upgrade to retire ones the new baseline covers.
solid answer
~50 sI'd treat overrides as tracked technical debt against a shared baseline. First, standardize consumption: a company platform/BOM (or shared parent) that imports spring-boot-dependencies, so every service starts from the same tested set and overrides are visible in one place. Second, make overrides explicit and justified — each pin carries a comment/ticket referencing the CVE or bug, and is as surgical as possible (bump the inner BOM for lockstep families, prefer patch bumps, never downgrade Spring Framework). Third, enforce in CI: run dependency-tree/convergence checks, dependency-vulnerability scanning, and integration tests so a drift or conflict fails the build, not production. Fourth, prefer upgrading Boot over overriding when a newer Boot patch already fixes the dependency. Fifth, on every Boot upgrade, audit existing overrides and delete the ones the new baseline subsumes. Governance = visibility + justification + verification + retirement.
go deeper
Understand that overrides should be rare, justified, and eventually removed.
Know the mechanics per build tool and that overrides need tests and tracking.
Design the CI checks (convergence, scanning, tests) and the surgical-override discipline for a service.
Own the org-wide model: shared platform/BOM, justification+CI enforcement, upgrade cadence, and systematic retirement of overrides across many services.
## Framing: overrides are debt against a baseline Spring Boot gives a curated, mutually-tested dependency set. Across many services the leadership goal is: let teams patch urgent issues (CVEs, bugfixes) **without** each service quietly forking off the baseline in incompatible ways. Treat every override as debt that must be visible, justified, verified, and eventually retired. ## 1. Standardize consumption - Publish a **company platform/BOM** (Gradle platform or Maven BOM) that imports `spring-boot-dependencies` plus any approved org-wide overrides, so all services inherit one curated baseline and per-service overrides are the exception, centralized and reviewable. - Pick one consumption model (shared parent vs imported BOM) and document the override mechanics for it (property override for parent; dependencyManagement-before-import for BOM; `ext`/`strictly()` for Gradle) so teams don't misuse a mechanism that silently no-ops. ## 2. Make every override explicit and minimal - Each override must carry a **reason**: CVE id / ticket, date, and an owner. No 'latest is better' pins. - **Surgical scope:** bump the inner BOM for lockstep families (jackson-bom, reactor-bom) rather than a single jar; prefer patch bumps within the same minor; **never** downgrade Spring Framework below what Boot requires. - Prefer **rich-version/strictly()** (Gradle) so conflicts fail loudly, over blunt `resolutionStrategy.force` which hides them. ## 3. Enforce in CI - **Convergence / dependency-tree checks** (`mvn dependency:tree`, Maven Enforcer `dependencyConvergence`, `gradle dependencies`) to catch conflicting transitive versions. - **Vulnerability scanning** (e.g. dependency-check / equivalent) to know when an override is actually needed and when it's redundant. - **Integration tests** on the surfaces the override touches so binary/behavioral drift fails the build instead of surfacing as runtime `NoSuchMethodError`. - Optionally a **lint/review rule** that flags any manual `<version>`/force so it gets human eyes. ## 4. Prefer upgrading Boot When a newer Boot patch/minor already ships the fixed dependency, upgrading Boot is better than a manual override — the service returns to a tested set. Keep Boot upgrades on a regular cadence (e.g. renovate/dependabot PRs) so 'just upgrade Boot' is usually available. ## 5. Retire overrides on every Boot bump Overrides rot: a later Boot baseline may match or exceed the pinned version, making the override redundant or, worse, a downgrade. On each Boot upgrade, **audit and remove** overrides the new baseline subsumes. This is the step teams most often skip, causing accumulating drift. ## 6. Communicate the trade-off Make the 'tested set' concept explicit in engineering guidelines so teams understand *why* the friction exists: the override buys a fix now but costs the free compatibility guarantee, and unmanaged overrides multiply into upgrade pain. ## Summary Governance is not 'ban overrides' — it's **visibility (centralized/commented), justification (CVE/ticket), verification (CI convergence + scan + tests), and retirement (audit each Boot upgrade)**, with a bias toward upgrading Boot itself.
- How do you keep overrides from silently rotting over time?Audit them on every Spring Boot upgrade: the new baseline may already include or exceed the pinned version, making the override redundant or even a downgrade. Remove subsumed overrides, and keep Boot upgrades on a regular automated cadence so 'just upgrade Boot' is usually an option.
- What CI checks catch the failure modes that overrides introduce?Dependency-convergence / dependency-tree checks (e.g. Maven Enforcer dependencyConvergence) for transitive conflicts, dependency-vulnerability scanning to justify/retire pins, and integration tests over the affected surfaces so binary/behavioral drift fails the build instead of production.
saying these in an interview costs you the question
- Banning all overrides (blocks urgent CVE fixes)
- Letting each service pin freely with no justification or tracking
- Never revisiting overrides after Boot upgrades
- Relying on force everywhere so conflicts stay hidden
- Treating a manual pin as permanent rather than debt to retire