Why Dependency Updates Are Essential to App Maintenance

Imagine a cricket ground that skips routine pitch maintenance because the surface still looks playable.

After the first match, a few worn patches appear. Nothing serious. The next match goes ahead, then another. Each time, the grounds team decides the pitch can manage a little longer without major attention.

Then an important tournament approaches.

What should have been ordinary preparation—repairing a few worn areas and getting the surface match-ready—turns into a much larger job. The cracks have deepened, the surface has become uneven, and fixing one problem now exposes another. Instead of making a small adjustment between matches, the team is dealing with accumulated deterioration just when the pitch needs to be at its most dependable.

Software dependencies age in much the same way.

An application can skip one library update without anything visibly going wrong. It can skip the next framework release and continue serving users normally. A runtime falls a version behind, then two. Build tools, SDKs and plugins continue moving while the application stays where it is.

For a while, everything still works. The real cost appears when the application finally has to move.

Perhaps a security fix requires a supported version of a library. That version needs a newer runtime. The runtime requires a newer build tool, while an older plugin no longer works with it. A dependency update that should have been routine has now become a chain of migrations, compatibility changes and regression testing. That is why dependency updates belong in application maintenance.

Their value is not simply that they give an application newer software. Regular updates prevent small, manageable version gaps from accumulating into a much larger maintenance problem.

A healthy dependency strategy keeps the application close enough to the software ecosystem around it that necessary change remains ordinary engineering work rather than an emergency modernization project.

Dependency maintenance is the ongoing work of identifying, evaluating, testing and adopting appropriate updates to the libraries, frameworks, runtimes, SDKs, build tools and other third-party components an application relies on.

The objective is not to run the newest version of everything immediately. It is to keep the application sufficiently current that it remains secure, compatible, supportable, buildable and reasonably easy to change.

NIST's Secure Software Development Framework recommends checking third-party components throughout their life cycle, including whether they remain maintained, whether known vulnerabilities exist and what action is needed when a component reaches end of life.

OWASP goes further in its current Top 10:2025 Software Supply Chain Failures, recommending an ongoing process for tracking direct and transitive dependencies and applying updates throughout the lifetime of an application.

For Levelworks, dependency maintenance maps most directly to Current & Updated App Health, while also supporting Secure, Compatible, Supported, Tested and Seamlessly Updatable.

What counts as an application dependency?

When developers hear "dependency update," they often think of packages listed in files such as package.json, requirements.txt, pom.xml or Gemfile.

Those are only part of the dependency surface.

A modern application may depend on:

  • language runtimes;
  • frontend or backend frameworks;
  • open-source libraries;
  • database drivers;
  • authentication libraries;
  • payment and cloud SDKs;
  • build tools and compilers;
  • testing frameworks;
  • container base images;
  • CI/CD actions and plugins;
  • mobile platform packages;
  • indirect or transitive packages pulled in by other dependencies.

A direct dependency is one your project deliberately includes.

A transitive dependency arrives because one of those direct dependencies relies on something else.

For example:

Your application

   └── Library A

         ├── Library B

         └── Library C

                └── Library D

Your developers may have deliberately selected only Library A, but defects, vulnerabilities or compatibility constraints in Library D can still affect the application.

OWASP specifically recommends tracking both direct and nested dependencies rather than maintaining only an inventory of top-level libraries.

Dependency maintenance therefore starts with knowing what the application actually depends on—not simply what developers remember installing.

Why dependencies become outdated even when the application does not change

An application can become outdated without a single line of its business logic changing.

The ecosystem around it continues moving.

Library maintainers release fixes. Runtimes add capabilities and deprecate older behavior. Browsers evolve. Mobile operating systems introduce new requirements. Cloud providers publish new SDKs. Framework versions enter and leave their support periods.

The dependency graph is therefore a moving system.

A version that was perfectly reasonable two years ago may now be:

  • missing important defect fixes;
  • outside its support window;
  • incompatible with a newer runtime;
  • blocking another component from being upgraded;
  • dependent on software that is itself obsolete;
  • increasingly difficult to test with current tooling.

This is why “the application still works” is a poor dependency-maintenance test.

It describes today's visible behavior while saying nothing about how difficult tomorrow's required change is becoming.

Dependency updates give you upstream fixes without maintaining them yourself

Applications inherit the strengths and weaknesses of the software they use.

A database driver may leak connections under an unusual failure condition. A UI component might contain an accessibility defect. A serialization library could mishandle an edge case. A networking package may have a bug triggered only under specific timeout conditions.

Your team can sometimes work around these problems locally.

But if the component's maintainers already fixed the underlying problem, staying sufficiently current allows you to consume that work rather than maintaining your own patch indefinitely.

There is an important qualification: a newer version can introduce its own regression.

Dependency updates should therefore be tested rather than trusted blindly.

The maintenance advantage comes from keeping version gaps small enough that adopting a useful upstream fix remains a normal application change instead of becoming a migration project.

Security updates are urgent—but security is not the whole argument

Security is the most obvious reason to keep dependencies updated.

A dependency that was considered safe when deployed can have a vulnerability disclosed years later, even though your own application code has not changed.

OWASP's 2025 Top 10 now places Software Supply Chain Failures at A03 and explicitly identifies outdated, unsupported and vulnerable components as risk conditions. Its guidance recommends continuously inventorying component versions, monitoring vulnerability sources and upgrading dependencies in a risk-based, timely manner.

NIST's SSDF similarly recommends ongoing checks for publicly known vulnerabilities and specifically advises organizations to ensure components remain actively maintained rather than reaching end of life unnoticed.

However, updating only when a severe vulnerability appears creates a poor maintenance model.

It means every dependency remains frozen until an emergency forces movement.

If your application is already four major versions behind when a critical security patch becomes necessary, remediation may first require unrelated runtime, framework and API migration work.

A healthier approach keeps dependencies reasonably close to maintained versions so that an urgent patch has a shorter path into production.

[Internal Link: “How Outdated Dependencies Create Security Risks in Your App”]

The biggest hidden cost is the widening upgrade gap

The cost of postponing an update often does not appear immediately.

It accumulates in the distance between where the application is and where the dependency ecosystem has moved.

Imagine an application uses version 2 of a library.

Version 3 introduces a small API adjustment. The team decides not to update.

Version 4 changes configuration behavior.

Version 5 stops supporting the application's old runtime.

Version 6 removes an API that the application still calls.

Eventually the company needs a feature available only in version 6.

The upgrade is no longer:

2 → 3

It is:

2 → 3 → 4 → 5 → 6

with several years of migration notes, deprecations and compatibility changes to understand simultaneously.

This is a form of dependency debt.

It does not necessarily break production today. It makes future change more expensive and less predictable.

Renovate's upgrade guidance recommends keeping dependencies reasonably current for this reason and, for severely outdated projects, taking major upgrades in sequence rather than jumping across several major versions at once.

The practical benefit of frequent dependency maintenance is therefore not simply receiving newer software.

It is keeping individual updates small enough to understand.

Version numbers can help estimate change risk

Many package ecosystems use Semantic Versioning, or SemVer, to communicate the intended scope of a release.

Under the official Semantic Versioning 2.0.0 specification:

  • a patch release contains backward-compatible bug fixes;
  • a minor release adds backward-compatible functionality;
  • a major release contains incompatible API changes.

For example:

4.2.1 → 4.2.2    Patch

4.2.1 → 4.3.0    Minor

4.2.1 → 5.0.0    Major

This gives maintenance teams a useful first indication of likely update scope.

It is not a guarantee.

Projects do not all follow SemVer perfectly. A patch can accidentally contain a regression, while a minor version can expose assumptions specific to your application.

Use the version number to classify the update, then read the release notes and test what actually changed.

Transitive dependencies make update problems less obvious

Sometimes a dependency cannot be fixed simply by changing its version.

Suppose:

Application

  └── Package A v2

       └── Package B v1

A problem is discovered in Package B, and the corrected version is B v2.

But Package A v2 only permits B v1.

You cannot safely resolve the deeper dependency without first finding a newer Package A that supports it.

GitHub's Dependabot documentation describes exactly this situation: a vulnerable transitive dependency may require upgrading its parent package because the existing parent's version constraints prevent the corrected transitive version from being selected.

This is why dependency graphs and lockfiles matter.

They reveal not only what the application explicitly requests, but what the build actually resolves.

A dependency-management process that checks only top-level packages can miss the components causing the real upgrade constraint.

Unsupported dependencies reduce the number of good choices

A maintained dependency gives your team options.

If there is a defect, you may be able to install a patch, report an issue, follow supported migration guidance or adopt a newer version.

An abandoned component leaves fewer choices.

Your team may have to:

  • maintain a private fork;
  • backport fixes;
  • replace the component;
  • accept the risk temporarily.

NIST's SSDF specifically recommends checking whether third-party components remain actively maintained and creating a plan for software that is no longer maintained or may soon become unavailable.

Support status therefore deserves monitoring alongside security vulnerabilities.

This is particularly important for frameworks and runtimes because they often underpin large areas of application code. Falling several generations behind at that level can make ordinary maintenance disproportionately expensive.

[Internal Link: “What Happens When Your App Uses an Outdated Framework?”]

Dependency maintenance protects compatibility

A dependency does not operate in isolation.

A library may require a particular runtime. A database driver needs to support the database version in production. A mobile SDK has to work with current mobile operating systems, while a build plugin must remain compatible with the tools used in CI.

This creates chains such as:

Application code

    ↓

Framework

    ↓

Runtime

    ↓

Operating environment

If one layer moves and another remains frozen, compatibility gaps appear.

A team may need to upgrade the runtime because its existing version has reached end of support, only to discover that the application's framework cannot run on the newer version.

Updating the framework then exposes packages that depend on the old framework behavior.

The dependency that seemed harmless because it continued working has now become a blocker for a different maintenance requirement.

Keeping dependencies reasonably current reduces the number of compatibility boundaries that need to be crossed at the same time.

Dependency updates protect the ability to build the application too

Runtime libraries are not the only components that age.

Your delivery toolchain can also drift.

Applications often rely on:

  • build plugins;
  • package managers;
  • CI/CD actions;
  • test runners;
  • linters;
  • compilers;
  • container base images.

Production may continue operating normally while the build process becomes progressively harder to reproduce.

A fresh developer machine may fail to build the project. A CI environment may stop supporting a plugin. An old base image can become unsupported.

At that point, the application still runs, but changing it safely has become harder.

OWASP's 2025 software supply-chain guidance specifically includes build tools, CI/CD, IDEs, artifact repositories and other development infrastructure within the components organizations need to track and maintain.

Dependency maintenance should therefore cover the complete path from source code to production, not only packages loaded at runtime.

Why “if it isn’t broken, don’t update it” eventually fails

The argument against dependency updates usually sounds reasonable:

Every update creates change. Change creates risk. If the existing version works, leave it alone.

The problem is that this treats the choice as:

change now

versus

never change.

For a maintained application, the real choice is usually:

make smaller controlled changes periodically

versus

make a much larger forced change later.

The second option can feel safer in the short term because nothing changes today.

It often creates more change at the exact moment the business has no choice but to upgrade.

Good dependency maintenance is therefore not aggressive updating for its own sake. It is keeping the application's distance from supported versions manageable.

Start with a dependency inventory

You cannot maintain components you cannot see.

At minimum, collect information from:

  • package manifests;
  • lockfiles;
  • runtime and framework versions;
  • container definitions;
  • important build tooling.

For larger applications or portfolios, a Software Bill of Materials (SBOM) can provide a more structured inventory.

NIST recommends using SBOM information to catalog third-party and open-source components and linking that inventory with vulnerability-detection capabilities.

OWASP similarly recommends maintaining an SBOM and continuously inventorying component versions.

Your team should be able to answer:

  • Which dependency versions are currently deployed?
  • Which are direct and which are transitive?
  • Which components are no longer maintained?
  • Which major updates are outstanding?
  • Which dependencies sit on critical business paths?
  • Which packages currently block another required update?

Without that visibility, dependency maintenance remains reactive.

Prioritize updates by risk rather than treating every package equally

A database driver used by every transaction deserves different attention from a test-report formatting library.

Useful prioritization factors include:

Factor

Higher priority when...

Security

A relevant serious vulnerability exists

Support

The installed version is nearing or past EOL

Criticality

The dependency affects core production workflows

Compatibility

It blocks a runtime, platform or framework upgrade

Version gap

Several versions have accumulated

Exposure

It processes sensitive or untrusted data

Migration difficulty

Delay is likely to make eventual replacement harder

This prevents dependency maintenance from becoming an exercise in making every update indicator green.

A package can legitimately remain one release behind while another needs immediate attention.

What matters is that the difference is deliberate.

Use automation to find updates before they become a project

Humans are bad at remembering to check dozens or hundreds of package ecosystems manually.

Dependency automation changes that.

GitHub's Dependabot version updates can monitor project manifests and create pull requests when newer versions are available. Importantly, GitHub distinguishes ordinary version updates from security updates; dependencies can therefore be kept current even when no known vulnerability is forcing the change.

Other tooling provides similar workflows.

The useful pattern is:

New dependency version

        ↓

Automated detection

        ↓

Update branch / PR

        ↓

CI + tests

        ↓

Engineering review

        ↓

Controlled release

Automation removes the discovery burden.

It should not remove judgment.

Read the release notes before trusting the version bump

A dependency update may look like a one-line change:

library: 4.7.1 → 4.8.0

But that line may replace thousands of lines of code maintained outside your organization.

Before adopting meaningful updates, check:

  • release notes;
  • migration instructions;
  • deprecated APIs;
  • configuration changes;
  • runtime requirements;
  • known issues;
  • behavior changes relevant to your use case.

Major releases deserve particular attention, but patch and minor releases should not be assumed risk-free.

The dependency's maintainers understand what they intended to change.

Your team understands how your application actually uses it.

Safe updating requires both perspectives.

Test dependency changes where they can affect behavior

A successful build proves only that the dependency can be compiled or installed.

It does not prove that the application behaves correctly.

Testing should reflect the role of the dependency.

For example:

Dependency updated

Useful validation

Authentication library

login, logout, SSO, sessions

Database driver

transactions, pooling, timeout behavior

Payment SDK

authorization, failure paths, refunds

UI component library

important screens, accessibility, browser behavior

Serialization library

API contracts and representative payloads

Build tool

clean builds and deployment artifacts

Strong regression coverage makes routine dependency updates much easier to accept because teams are not relying entirely on manual confidence.

This is also why projects with weak testing often fall further behind: every update feels dangerous because nobody can quickly verify whether existing behavior survived.

Update in manageable increments

When a project is badly behind, updating every dependency simultaneously creates a different form of risk.

Suppose 40 packages are upgraded and checkout starts failing.

Which update caused it?

Grouping related packages can make sense when they are designed to move together. Unrelated major upgrades are usually easier to review separately.

For projects several years behind, moving through important major releases in sequence can also make migration behavior easier to understand. Renovate recommends this approach for significantly outdated projects rather than jumping straight across several major versions.

The ideal dependency change is large enough to make useful progress but small enough that the team can explain what changed.

Keep builds reproducible while keeping dependencies current

Keeping dependencies updated does not mean installing whatever happens to be newest whenever production builds.

Version constraints and lockfiles exist to provide reproducibility.

For example, npm's current update mechanism updates packages while respecting the semantic-version constraints defined by the project and its dependencies.

The maintenance process should separate two goals:

Currency: Are we deliberately adopting appropriate newer versions?

Reproducibility: Can we identify and reproduce the exact dependency versions used by a build?

You need both.

Without currency, the application drifts.

Without reproducibility, every build can become an uncontrolled dependency experiment.

Remove dependencies that no longer provide value

Dependency maintenance should not consist only of upgrading packages.

Sometimes the correct maintenance action is deletion.

Applications accumulate dependencies introduced for:

  • experiments;
  • features that no longer exist;
  • one-time migrations;
  • capabilities now provided elsewhere.

Unused dependencies still increase the software inventory, create update work and can increase security or compatibility exposure.

OWASP's current guidance explicitly recommends removing unused dependencies and unnecessary components as part of reducing software supply-chain attack surface.

A useful dependency review therefore asks two questions:

Does this need updating?

and

Do we still need it at all?

Know when an update should be deferred

Healthy dependency maintenance does not mean merging every release immediately.

An update may reasonably wait when:

  • a major release requires deliberate migration work;
  • the new release has a known regression affecting your application;
  • the change lands during a critical business freeze;
  • the team needs more evidence before adopting a very recent high-risk version.

The important difference is between deferral and neglect.

A deferred dependency should have a documented reason, an owner and a point at which the decision will be reviewed.

Without that, temporary exceptions become permanent version drift.

Track whether dependency debt is growing

A few simple indicators can reveal whether the maintenance process is working.

Consider monitoring:

  • number of dependencies past end of support;
  • number of pending major-version upgrades;
  • age of the oldest important pending update;
  • critical dependencies several major versions behind;
  • time taken to adopt important patches;
  • updates blocked by incompatible application code;
  • repositories without automated dependency monitoring.

Do not optimize these numbers mechanically.

Three consciously deferred updates can be healthier than zero outstanding updates achieved by automatically merging everything.

The purpose of measurement is to identify when the application's ability to update is gradually deteriorating.

A sustainable dependency maintenance workflow

A practical process looks like this:

  1. Inventory dependencies. Include direct, transitive, runtime and build-time components that materially affect the application.
  2. Monitor continuously. Detect new releases, vulnerabilities and lifecycle changes automatically where possible.
  3. Prioritize by risk. Raise security issues, approaching EOL, compatibility blockers and critical production dependencies first.
  4. Review the change. Read release notes and migration guidance rather than treating the version bump as the complete change.
  5. Update incrementally. Keep version gaps small enough that upgrades remain understandable.
  6. Test affected functionality. Match the validation to the dependency's role in the application.
  7. Release normally. Dependency changes should use the same controlled deployment discipline as your own code.
  8. Observe production. Watch the workflows and technical signals most likely to be affected.
  9. Record intentional deferrals. Keep postponed updates visible rather than letting them disappear into backlog.
  10. Remove unnecessary components. Reduce the amount of third-party software the application must continue maintaining.

[Internal Link: “CI/CD for App Maintenance: How Automated Releases Reduce Risk”]

The healthiest dependency update is usually the boring one

Dependency updates become frightening when an application has accumulated years of version drift, an unclear dependency graph and weak regression coverage.

That fear encourages teams to postpone updates.

Postponement creates more drift.

The next update then becomes larger and riskier, reinforcing the belief that dependency updates are inherently dangerous.

The healthier cycle works in the opposite direction.

Dependencies are inventoried. New releases are discovered early. Updates remain relatively small. Automated tests provide feedback, while major migrations are scheduled before support disappears. If an urgent fix is needed, the application is close enough to supported versions that the team can reach it without first performing years of unrelated modernization.

For Levelworks, that is why dependency maintenance sits primarily within Current & Updated App Health.

It supports Secure because fixes remain reachable, Compatible because the application stays closer to the surrounding technology ecosystem, and Supported because critical components remain within maintained versions. Tested and Seamlessly Updatable matter because the strongest dependency strategy is one in which updating software becomes a predictable part of normal delivery rather than an exceptional event.

The aim is not to chase the newest release number.

It is to preserve the application's ability to move.

A dependency that is slightly behind can usually be updated through ordinary maintenance.

A dependency stack ignored for years can become a constraint on security work, platform upgrades, feature development and even the team's ability to build the application.

That is why dependency updates are not housekeeping around the edges of app maintenance.

They are one of the things that keep future maintenance possible.

‍

Continue Reading