By Lakshmi Priya Gopalsamy
Independent Researcher & Technology Lead, Software Engineering - USA
Most engineering teams have a version of this story: a CVE lands in a widely-used library, someone checks the codebase, and the dependency is eighteen months out of date. The fix is straightforward. The remediation sprint is not.
Dependency hygiene tends to get treated as housekeeping — the kind of work that belongs on a backlog somewhere, to be addressed between features. That framing is the root of the problem. Keeping dependencies current is not maintenance. It is a foundational software engineering practice, in the same category as writing tests, reviewing code, and managing technical debt. Teams that treat it that way avoid emergency remediations. Teams that do not eventually pay for every skipped update in one large, urgent bill.
This article makes the case for dependency hygiene as a first-class engineering discipline and describes a practical, low-noise system for building it into how a team works — illustrated with Gradle, Vela, and Mend SCA, but applicable to any modern software stack.
Why Dependencies Drift — and Why It Matters
Dependency drift is not laziness. It is a structural outcome of how engineering teams prioritize work. Features ship because features are visible. Dependency updates are invisible until something breaks. Without a system, updates happen when an engineer happens to notice, which means they happen irregularly, incompletely, and always reactively.
The cost compounds in three distinct ways.
Security exposure. Every unpatched dependency is a window. Common Vulnerabilities and Exposures (CVEs) are discovered continuously, and the window between public disclosure and active exploitation is shrinking. A team running six-month-old dependencies at any given time is carrying an unknown number of known vulnerabilities.
Migration difficulty. Patch updates are cheap. Minor updates are usually cheap. Major version migrations are expensive — and the further a codebase drifts from current, the more major versions accumulate between the current state and the target state. A team that skips three major versions of a framework is not facing one migration; it is facing three, with potentially conflicting breaking changes across all of them.
Cognitive overhead. Developers working with stale dependencies lose access to current documentation, current community knowledge, and current tooling compatibility. They work with outdated mental models. The codebase becomes harder to reason about over time, not because the code itself changed, but because the ecosystem around it has moved on.
The solution is not to chase every release. It is to establish a repeatable process that makes staying current the default state rather than a periodic catch-up effort.
The Core Design Principles
Before looking at tooling, it helps to establish what a good dependency hygiene process actually needs to do.
Automation handles discovery, not decisions. The work of checking which dependencies have updates, comparing versions, and preparing a diff is mechanical. Automating it is appropriate. The work of deciding whether to accept an update is an engineering judgment call. That stays with the team.
Updates are batched, not broadcast. One PR per dependency creates noise. Noise creates dismissal habits. Dismissed queues are worse than no queue at all. Updates should be grouped logically — by dependency family, by update type, or both — so each PR is reviewable as a coherent unit.
Scope is bounded by risk. Patch and minor updates follow semantic versioning conventions: they are not supposed to introduce breaking changes. Major updates explicitly may. Treating these two categories differently — auto-batching the former, flagging the latter for explicit review — reflects their actual risk profiles.
Nothing auto-merges. This is a firm principle, not a preference. Supply chain attacks against open source packages have demonstrated that even patch updates from established libraries can be compromised. Maintaining a human review step for all dependency changes is a small overhead with a meaningful security return.
The process is owned by the team, not by individuals. Dependency hygiene that depends on a particular engineer noticing and acting is fragile. A scheduled, automated process that surfaces updates to the whole team on a predictable cadence is resilient.
A Two-Part Implementation
The following describes a concrete implementation using tools common in JVM-based enterprise environments. The pattern generalizes to other stacks — npm/Dependabot, Python/pip-audit, Go/govulncheck — but the specific mechanics are grounded in Gradle, Vela CI, and Mend SCA.
Part 1: Scheduled Version Scanning
A weekly Vela pipeline job handles the routine work of catching up to stable releases.
The job scans Gradle dependencies against the latest stable versions published to the configured registries. It updates gradle.properties with all eligible patch and minor version bumps. Major version bumps are excluded from the automated update — instead, they are written to a report artifact surfaced in the PR description, so the team is aware of pending major migrations without being pressured to merge them.
The output is a single batched PR opened against the main branch, covering all patch and minor updates discovered in that week's scan. The PR description includes a changelog summary: which libraries moved, from what version to what version, and any flagged major versions that were skipped.
Review time for this PR, when the team has established a cadence, is typically under thirty minutes. Dependencies within a single release cycle are predictable in their scope. Engineers learn quickly that the weekly dependency PR is low-risk to approve and expensive to defer.
The job requires no new secrets and no additional tooling approvals beyond what a standard Vela pipeline already has access to. It fits inside the existing CI infrastructure rather than requiring a parallel system.
Part 2: Security Scanning and Grouped Remediation
The second mechanism addresses CVEs directly. Mend SCA, integrated with Renovate on GitHub, provides continuous scanning of dependency manifests against known vulnerability databases and generates remediation PRs when fixes are available.
The critical configuration decision is grouping. Without grouping, Renovate generates one PR per vulnerable package — potentially dozens per scan cycle, each requiring individual review and merge. With grouping configured by dependency family, the team receives a small number of coherent remediation PRs.
In a typical JVM service this means groups along lines like: Micronaut framework modules, Kafka client libraries, Kotlin standard library and coroutines, logging infrastructure (Log4j, Logback, SLF4J), and test tooling (JUnit, MockK, Kotest). Each group maps to a natural ownership boundary within the team. The engineer most familiar with the Micronaut integration reviews the Micronaut group PR. The test infrastructure group PR goes to whoever owns that layer.
This grouping also reflects how CVE remediation actually works. Vulnerabilities in a library family often require coordinated updates across multiple artifacts under the same group ID. Treating them as a unit in the PR structure matches the actual remediation unit, which reduces the chance that a partial fix leaves a related artifact still vulnerable.
Integrating Into Engineering Workflow
A dependency hygiene process that exists outside normal engineering workflow will be treated as optional. One that is integrated into it will not.
Practically, this means a few things. The weekly dependency PR should appear in the same review queue as feature PRs. It should have an owner — not a permanent owner, but a rotation, so the work is shared and the team develops collective familiarity with the dependency landscape. Major version flags surfaced by the automated scan should be tracked in the backlog, not ignored, so that migration work gets planned rather than deferred indefinitely.
The process also needs a definition of done at the team level. A useful one: no dependency more than two minor versions behind current stable, no open critical CVEs older than the remediation SLA defined by the organization's security policy. These are measurable. They can appear on dashboards. They give the team a clear signal of when the process is working and when it needs attention.
The Actual Goal: Predictability
Dependency hygiene is ultimately about reducing variance. Teams that stay close to stable releases have a smaller and more predictable CVE exposure window. They spend less engineering time on emergency remediations. They face smaller, cheaper major version migrations when they do occur. They work with dependencies whose documentation and community support are current.
None of this requires chasing every release or treating every update as urgent. It requires a process — scheduled, automated in the right places, grouped intelligently, and reviewed by humans — that makes staying current the default rather than the exception.
The goal is not zero dependency lag. It is a predictable cadence that keeps the team ahead of the curve rather than reacting to it. That is a software engineering fundamental. It deserves to be treated like one.

Top comments (0)