Why Nobody Updates Your Dependencies (and What It Costs When Something Breaks)

#outdated dependencies risk

Founder & Lead Developer

Expert in software development and legacy code optimization

LinkedIn

Every engineering team we have audited in the last two years had the same line somewhere in their backlog: "update dependencies." Most of them had it for months. Some had it for over a year. The outdated dependencies risk this creates is not a mystery to anyone involved. Everyone knows the libraries are old. Nobody can quite explain why the update never happened, because no single decision caused it. It was a dozen small ones, each reasonable on its own.

This post is about that gap, why it opens up even on teams that care about security, and what it actually costs once a dependency finally breaks something in production.

Updates do not get skipped. They get postponed.

Nobody on a team decides, in a meeting, to leave a vulnerable package in production. What happens instead looks more like this: a sprint planning session runs long, and "update the auth library" gets bumped to next sprint because there is a client deadline. Next sprint has its own deadline. The person who usually handles dependency updates changes teams. The new hire does not know the update checklist exists. Six months pass, and the task that would have taken ninety minutes now needs a half day, because three more releases have shipped in the meantime, each with its own breaking changes stacked on top of the last.

This is the part that makes outdated dependencies risk different from most other technical debt. A messy function sits there, quietly getting worse, but it does not get more expensive to fix just because time passes. A dependency does. Every week without an update adds another release to catch up on, and most package maintainers do not write changelogs with the next person's upgrade path in mind. They write them for people who update regularly. Skip four releases of a framework and you are no longer reading one changelog. You are reconstructing four of them and hoping the order you apply them in does not matter.

The pattern repeats because updating dependencies has no natural owner. It is not a feature a product manager will prioritize, since it ships nothing visible. It is not a bug a customer will report, since nothing is broken yet. It sits in that unowned space between "someone's job" and "everyone's problem," which in practice means it is nobody's job until something forces the issue.

What the OWASP data actually says about this

Vulnerable and outdated components has been one of the OWASP Top 10 web application security risks since the list existed in its modern form, and it keeps earning that spot for a simple reason: most breaches that start with a dependency are not zero-days. They are known vulnerabilities, published with a CVE number and a patched version available, sitting unaddressed in a production system for months after the fix shipped. The attacker does not need to find anything new. They need to find a team that has not updated.

This matters for how a non-technical decision maker should think about the problem. "We might get hacked by something exotic" is not the realistic threat model. "We are running a version of a library with a documented, public vulnerability, and anyone can check which version we are using from the HTTP headers or a public repository" is closer to it. The fix is not a research project. It is almost always: run the update, run the tests, ship it. The hard part is never the patch. It is remembering, and finding the hour, before someone else finds the gap first.

Why the bill grows every month it waits

Picture two companies with the identical outdated library in production. Company A updates it the week the new version ships. Company B updates it fourteen months later, after a security scan flags it during due diligence for a funding round.

Company A's update takes an afternoon. One version jump, one changelog, a handful of tests to confirm nothing broke.

Company B's update takes a different shape entirely. Fourteen months of releases means several major version bumps, each with its own deprecated APIs and renamed functions. The original developer who wrote the integration may no longer be at the company. The test coverage that would have caught a regression quickly may not exist for that part of the codebase, because it was written before anyone thought it needed tests. What should have been a routine patch becomes a multi-day migration project that competes for time with whatever the business actually wants shipped that quarter, at exactly the moment someone outside the engineering team is asking uncomfortable questions about security posture.

We covered the broader version of this problem, what happens when a dependency itself turns out to be compromised and you need to prove to a customer or auditor that you were not exposed, in our guide to supply chain security for mid-size SaaS teams. The short version applies here too: the cost of an update does not stay flat while you wait. It compounds, and the compounding is the actual danger, more than any single vulnerability.

What a working update cycle looks like

The fix is rarely more tooling. Most teams we talk to already have Dependabot or Renovate opening pull requests. The pull requests pile up unreviewed, which is a different problem than not having the tooling at all. A bot that opens forty pull requests nobody merges has not reduced the outdated dependencies risk. It has just made the backlog easier to count.

What actually closes the gap is a recurring cycle with a named owner, whether that is someone on your own team with protected calendar time each month, or an outside partner running it as a standing responsibility. The cycle itself is unglamorous: review what changed upstream, update in small batches rather than all at once, run the test suite, fix what breaks while the change set is still small enough to reason about, and ship it before the next batch arrives. Teams that do this monthly rarely have a dependency crisis. Teams that do it "whenever there is time" eventually do, because "whenever there is time" has a way of meaning "never" until an external deadline forces it.

If your codebase has reached the point where updates feel too risky to attempt without a dedicated project, that is usually a sign the underlying code needs attention as much as the dependency list does. A code quality audit can tell you how much of the gap is tooling and process versus how much is structural debt in the codebase itself, and a legacy code optimization engagement is the right next step when the honest answer is both.

A quick way to check where you stand

You do not need to read code to get a rough sense of your own exposure. Ask whoever maintains your main application two questions: when was the last time dependencies were updated, and is there a recurring time set aside for it, or does it happen only when something forces it. The first answer tells you how deep the backlog already is. The second tells you whether it is about to happen again.

A team that updates every month and can point to the last run is in reasonable shape, even if a few packages are a version or two behind. A team where the honest answer is "it has been a while, and we do not have a set time for it" is carrying a debt that grows on its own, whether or not anyone has written it down as a risk.

If the backlog has already grown past what feels manageable with the time your team has, we run exactly this kind of monthly update cycle for clients who would rather hand the process to someone with a checklist than keep rediscovering it under pressure. Reach out at hello@wolf-tech.io, or have a look at what we do at wolf-tech.io. The conversation costs nothing, and knowing where you actually stand usually takes less time than the update you have been postponing.