Skip to main content
  1. Posts/

DevSecOps: dependency updates without noise

Author
m58
0xm58.xyz
Table of Contents
Dependency updates are security work, but only if the process is quiet enough that people keep reading the diffs.

The usual failure mode is not a missing bot. It is too many pull requests, no triage, and no clear rule for what gets merged first.

A better setup is boring:

  • small update batches
  • clear severity rules
  • tests that run before review
  • ownership for ignored findings
  • a monthly cleanup slot for low-risk drift

Separate security from hygiene
#

Treat known vulnerabilities differently from normal version drift.

Security updates should answer:

  • is the vulnerable package reachable?
  • is the vulnerable function used?
  • is there a fixed version?
  • does the fix contain breaking changes?
  • can we patch, replace, or isolate the dependency?

Routine updates have a different goal: keep the upgrade path short. They do not need the same urgency unless they remove old transitive dependencies or unblock a security fix.

Keep pull requests small
#

One giant dependency PR is easy to open and hard to review. It mixes risk across packages, makes test failures unclear, and encourages blind merges.

Useful grouping:

  • patch and minor updates together when tests are strong
  • major updates in their own PR
  • runtime dependencies separate from dev tooling
  • security fixes separate from routine updates

This makes rollback easier. It also makes ownership clearer when something breaks.

Make CI do the first review
#

Before a human reads the diff, CI should already have checked the basics:

  • lockfile is valid
  • unit tests pass
  • build passes
  • dependency audit runs
  • container scan runs if an image is built
  • license checks run if the company cares about license policy

Do not put all security decisions in CI. Use CI to remove obvious bad updates and surface the reason.

Triage vulnerabilities like bugs
#

Every vulnerability should end in one of four states:

  • fixed
  • not reachable
  • accepted until a date
  • blocked by a named reason

“Ignored” is too vague. If a finding is accepted, write down why, who owns it, and when it gets checked again.

Good acceptance notes are short:

Accepted until 2026-09-30. Package is used only in a build-time path.
No runtime exposure in production image. Revisit after framework upgrade.
Owner: platform-security.

That gives the next reviewer enough context without turning the ticket into an essay.

Watch transitive dependencies
#

Most noisy findings come from transitive dependencies. The fix might be:

  • update the direct parent package
  • override the transitive version
  • remove an unused dependency
  • wait for upstream and add a dated exception

Do not start with overrides as the default. They can hide compatibility problems. Use them when the risk is clear and tests cover the affected path.

Use a simple merge rule
#

For small services, this rule works well:

  • critical and high reachable vulns: patch immediately
  • critical and high non-reachable vulns: document and review soon
  • medium vulns: batch weekly
  • low vulns and routine drift: batch monthly
  • major version bumps: schedule with the owning team

The exact thresholds can change, but the rule should be written down. Otherwise every update becomes a fresh argument.

What I would automate first
#

Start with the parts that reduce review fatigue:

  • one scheduled dependency update window per week
  • auto-close superseded bot PRs
  • labels for security, major, runtime, and dev-only updates
  • CI comments that show audit results and changed package paths
  • dashboards for accepted findings that are near expiry

Automation should make the work smaller. If it only creates more tickets, it is not helping.

Summary
#

Dependency management is part of DevSecOps because it sits between engineering speed and production risk.

Keep the process simple: separate security from hygiene, keep PRs small, let CI reject obvious bad updates, and make every accepted risk expire. The main goal is not perfect freshness. The goal is a codebase where important updates still get reviewed carefully.

Related

Rust basics: Option and Result

·480 words·3 mins
Rust does not use null for ordinary absence, and it does not rely on exceptions for recoverable errors. Instead, it uses enums: Option<T> and Result<T, E>. These two types show up everywhere. Once they are clear, a lot of Rust APIs read more plainly.