Safeguard
Developer Tools•

How do you lock dependency versions in a legacy Java build?

Lock Java dependency versions with Gradle dependency locking or Maven dependencyManagement and the enforcer plugin so scans match what ships.

Safeguard Security Team
3 min read

In Gradle, enable dependency locking so resolved versions are saved to a lock file. In Maven, pin versions with dependencyManagement or a bill of materials and enable the enforcer plugin's dependencyConvergence rule. Either way, builds become repeatable and your scans describe what actually ships.

Why locking matters before a split

Legacy builds often use dynamic versions or let transitive libraries float. Gradle's documentation describes the risk directly: with dynamic versions such as 5.+, the resolved version can change over time, so builds can break unexpectedly. For security, the problem is that the scan you ran on Monday may not describe the build produced on Friday.

Before you extract services, a locked, reproducible build gives you a trustworthy baseline, and the new services start with the same discipline.

Gradle: dependency locking

Gradle explains that dependency locking saves the resolved versions of dependencies to a lock file, so later builds use the same versions and the dependency graph does not change unexpectedly.

  1. Activate locking for the configurations you want, or for all of them.
  2. Generate the lock state with Gradle's write locks option, which saves what currently resolves.
  3. Commit the lock files.
  4. Update locks deliberately, selectively when possible, as part of a reviewed change.

Check the Gradle guide for the exact commands and lock modes in your version.

Maven: pinning and convergence

Maven has no lock file by default. The usual approach is:

  1. Use dependencyManagement or a bill of materials to specify one version for each artifact, including transitive ones. The Maven enforcer documentation shows how this pins a version regardless of what transitive paths request.
  2. Enable the dependencyConvergence rule in the enforcer plugin. It fails the build if two dependencies pull different versions of the same artifact, and prints the paths so you can add an exclusion or a pin.
  3. Avoid version ranges and snapshot dependencies in release builds.

What to do with the result

StepWhy
Commit lock files or pinsReviewers see every version change
Scan the locked resultFindings match the shipped build
Update on a scheduleLocks must not freeze vulnerable versions forever
Fail CI on driftSurprises are caught early

The trade off

Locking stops silent upgrades, including silent security fixes. That is why you need a routine to update. Many teams use a weekly job that proposes lock updates as pull requests, reviewed like any other change.

Common legacy problems

  • Parent POMs from other teams that pin old versions
  • Excluded dependencies that were never added back
  • Shaded or fat archives that bundle libraries, hiding them from the lock file. Scan the final artifact as well as the build file.

Related: why the same library appears in many versions after a split.

Safeguard's dependency scanning and SBOM generation read resolved dependency data, and policies can flag unpinned versions.

Quick check

Does locking make me safer?

It makes builds repeatable and scans accurate. Safety also needs a routine to update locked versions.

Do fat archives need special handling?

Yes. Libraries bundled inside them may not appear in the lock file, so scan the built artifact too.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.