Skip to content
Production Ready — Part 30 of 30

Dependency Updates: Stay Current Without Breaking Things

Written by claude-sonnet-4 · Edited by claude-sonnet-4
dependenciesrenovatedependabotnpmsupply-chainproductionmaintenance

Production Ready -- Part 30 of 30


On September 8, 2025, a maintainer of debug, chalk, and sixteen other widely-used npm packages received an email that looked like a routine npm security alert. It asked him to update his two-factor authentication. He complied. Within minutes, attackers had his username, password, and a live TOTP code -- enough to log in, publish malicious versions of his packages, and inject cryptocurrency-wallet hijacking code into libraries downloaded billions of times per week.

The malicious versions were live for two hours. According to analysis by Upwind Security, that was enough time for thousands of developers to pull them. The @ctrl/tinycolor package -- 2.2 million weekly downloads -- was separately compromised in the same campaign. Packages like error-ex followed days later via a similar account takeover.

This is the world your production app lives in.

Dependency management is not glamorous. It is the unglamorous, mandatory, never-finished work of keeping your house in order. It is also, as that September 2025 campaign showed, one of the most consequential things you will do. Thirty days ago you started this series to become production-ready. This is the last piece.


The Two Failure Modes

Every team ends up in one of two bad places.

Failure mode one: stale dependencies. You ship, you move on, and you never touch package.json again unless something breaks. Six months later you have seventeen high-severity vulnerabilities in npm audit, a major version gap on your core framework, and a migration that will now take a week instead of an afternoon. Security patches pile up. Breaking changes pile up. When you finally have to upgrade, you are upgrading through multiple major versions simultaneously and debugging cascading failures you cannot untangle.

Failure mode two: blind updates. You accept every Dependabot PR the moment it opens, auto-merge everything, and ship it to production. One morning a patch-level release of axios pulls in plain-crypto-js@4.2.1 -- a package that, as Socket Research documented in March 2026, deployed a remote access trojan via a postinstall hook. Patch version. No breaking API changes. Straight to your production servers.

Neither "never update" nor "always update immediately" is a strategy. The goal is a disciplined rhythm that keeps you current without being reckless.


Semantic Versioning Is a Contract

Before you build a strategy, you need to understand what version numbers actually mean.

MAJOR.MINOR.PATCH -- for example, 2.14.3:

  • PATCH (2.14.3 -> 2.14.4): Bug fixes only. No API changes. Generally safe to update with minimal review.
  • MINOR (2.14.3 -> 2.15.0): New features added in a backward-compatible way. Your existing code should not break, but test it.
  • MAJOR (2.14.3 -> 3.0.0): Breaking changes. APIs have changed. Read the changelog. Test thoroughly. Update one major version at a time.

The caret (^) in your package.json -- "express": "^4.18.2" -- means "accept minor and patch updates but not major." The tilde (~) means "patch updates only." Understanding this matters because npm's defaults allow more than most developers realize.


The Five Practices

1. Commit Your Lock File -- Always

package-lock.json and pnpm-lock.yaml exist for one reason: reproducible installs. When you commit the lock file, everyone on your team -- and your CI pipeline, and your production deployment -- installs exactly the same dependency tree. Without it, a npm install today and a npm install next Thursday may resolve to different transitive dependency versions.

Do not put lock files in .gitignore. If you have done this, fix it today.

Use npm ci in CI/CD environments instead of npm install. npm ci installs exactly what is in the lock file and fails if the lock file is out of sync with package.json. It is faster and deterministic.

# In CI/CD -- use this
npm ci

# Locally, when you want to add/update a package
npm install some-package
git add package.json package-lock.json
git commit -m "chore: add some-package"

2. Automate Dependency PRs

You will not remember to check for updates manually. Automate it.

Dependabot is the zero-friction option if you are on GitHub. Add .github/dependabot.yml:

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "tuesday"
    groups:
      minor-and-patch:
        update-types:
          - "minor"
          - "patch"
    open-pull-requests-limit: 10

Renovate is the more powerful option, especially for teams managing multiple repos or complex monorepos. It supports over 90 package managers to Dependabot's 30+. Here is a renovate.json for a Next.js/Supabase project:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:best-practices"],
  "schedule": ["every tuesday"],
  "minimumReleaseAge": "14 days",
  "packageRules": [
    {
      "matchPackagePatterns": ["^next", "^react", "@supabase/"],
      "groupName": "core framework",
      "automerge": false
    },
    {
      "matchUpdateTypes": ["patch"],
      "matchPackagePatterns": ["eslint", "prettier", "typescript"],
      "groupName": "dev tooling patches",
      "automerge": true
    }
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "schedule": ["at any time"]
  }
}

The minimumReleaseAge: "14 days" setting is critical. Renovate's own best practices documentation recommends it specifically for automerge scenarios: it gives the npm registry time to pull malicious packages before they reach your codebase. Two weeks is enough for most compromised releases to be identified and removed.

3. Update Tuesday

Pick one time slot per week to review dependency PRs. Tuesday works well because it gives you Monday to clear your head and the rest of the week to deal with anything that breaks.

The ritual is short:

  • Open your Dependabot or Renovate dashboard
  • Merge patch and minor updates that pass CI
  • Flag major version updates for deeper review
  • Reject or defer anything that looks unusual

Do not let PRs age. A month-old Dependabot PR means your lock file is a month behind. Stale PRs pile up, get overwhelming, and get ignored. Keep the queue short.

4. Major Version Updates: Slow Down

When react-router or next releases a major version, do not merge it the day it appears. Read the official migration guide. Check GitHub issues for the first week to see what breaks in the wild. Update it in a branch, run your full test suite, deploy to a preview environment, and smoke test your critical paths before merging.

Update one major dependency at a time. Never batch major upgrades. If next@15 and @supabase/auth-helpers@1 are both waiting, do them in separate PRs on separate days. Debugging two major API changes simultaneously is how engineers lose entire weekends.

5. Security-Only Updates

For patch-level security fixes, do not wait for Update Tuesday. Run npm audit regularly and npm audit fix to apply patch-level security fixes immediately:

# Check for vulnerabilities
npm audit

# Auto-fix patch-level issues (safe -- stays within semver range)
npm audit fix

# Check what would change before running
npm audit fix --dry-run

# Force-fix even if it requires going outside semver range
# Use with care -- review the changelog first
npm audit fix --force

npm audit fix without --force respects your semver ranges and will not make breaking changes. It is the safe, fast path for closing known vulnerabilities.


The Pinning Debate

Should you pin exact versions ("express": "4.18.2") or use ranges ("express": "^4.18.2")?

The argument for pinning: you know exactly what is running. No surprises from transitive updates. The argument for ranges: you automatically receive security patches.

The practical answer for most production apps: pin your direct dependencies in package.json using exact versions, rely on your lock file for the full tree, and use an automated tool like Renovate or Dependabot to open PRs when you need to move pins forward. This gives you the reproducibility of pinning with the automation of ranges. Red Hat's engineering teams used broad version pinning as the primary mechanism that protected customers during the September 2025 npm supply chain attacks.


Monitoring for Supply Chain Attacks

Lock files and automated PRs protect you from drift. They do not protect you from a trusted maintainer's account being phished at 1 AM.

Two tools to add to your workflow:

socket.dev: Install the GitHub app. It analyzes every dependency PR for behavioral signals -- postinstall scripts that make network calls, obfuscated code, packages that just appeared on npm minutes ago, environment variable exfiltration. It caught the Shai-Hulud campaign packages in under six minutes. Free for open source projects.

npm audit signatures: Run this in CI to verify that installed packages have valid registry signatures and provenance attestations. npm's Trusted Publishing, which reached general availability in July 2025, now automatically attaches cryptographic provenance to packages. A package that fails signature verification was either tampered with or pre-dates the provenance system.

# Add to your CI pipeline after npm ci
npm audit signatures

Testing After Updates

Every dependency update is a code change. Treat it like one.

  1. Run your full test suite on every dependency PR.
  2. Deploy to a preview or staging environment.
  3. Smoke test your critical paths: auth flow, checkout, primary API endpoints, whatever your users do first.
  4. For major version updates, expand testing to cover the migration guide's listed breaking changes explicitly.

If you do not have a test suite, that is the article you should have addressed after Part 5 of this series. The discipline compounds. Tests give you the confidence to merge updates quickly. Quick merges keep your dependency tree fresh. A fresh dependency tree means smaller, safer individual updates.


The Production Readiness Checklist

Before you close this tab, run through this:

  • Lock file (package-lock.json or pnpm-lock.yaml) is committed to git
  • CI/CD uses npm ci, not npm install
  • Dependabot or Renovate is configured with a weekly schedule
  • minimumReleaseAge (Renovate) or equivalent cooldown is set for automerge
  • A recurring "Update Tuesday" is on your calendar
  • npm audit runs in CI and fails the build on high/critical issues
  • npm audit signatures runs in CI after npm ci
  • socket.dev GitHub app (or equivalent) is installed on the repo
  • Major version updates are reviewed manually, one at a time
  • Preview deployments and smoke tests run before merging any dependency update

Thirty Days

That is it. You are done.

Over the past thirty articles, you have gone from zero to production-ready. You know how to test, deploy, monitor, roll back, cache, rate limit, and now maintain. You understand lock files and supply chain attacks. You have Renovate configured. You have Update Tuesdays.

That puts you ahead of most teams shipping software today. Not because those things are hard -- most of them are not -- but because most teams never stop to build the habits. You did.

The dependencies will keep coming. The attackers will keep getting more creative. But you have a system now. Run it.


Ask The Guild

What is your current dependency update habit? Are you team Dependabot or team Renovate? Have you ever been burned by a bad update -- or a stale one? Share your war story in the thread below. Thirty days of this community means the room is full of people who have been exactly where you are.

Copy A Prompt Next

Review and debug

If this article changed how you think about the problem, copy a prompt that turns that judgment into one safe, reviewable next step.

Matching public prompts

23

Keep the task scoped, copy the prompt, then inspect one reviewable diff before the agent continues.

Need the safest first move instead? Open the curated sample prompts before you browse the broader library.

Working With AI ToolsWorking With AI Tools

v0 by Vercel — UI Components From a Text Prompt

Generate production-ready UI components with v0 and integrate them into your projects.

Preview
"I want v0 to generate a React component for this screen:
[describe the UI, data fields, visual style, empty state, loading state, and mobile behavior]
The component must:
1. work in a Next.js + Tailwind project
2. be easy to wire to real data later
Production Ready

Use this production insight inside a full build sequence

Production articles show you what breaks in the real world. The right path turns that lesson into a sequence you can ship with instead of just nodding at.

Best Next Path

DevOps and Deployment

Guild Member · $29/mo

Connect the code to production: CI/CD, hosting, observability, DNS, and the runtime habits that keep launches boring.

25 lessonsIncluded with the full Guild Member library

Need the free route first?

Start with Start Here — Build Safely With AI if you want the workflow and vocabulary before you dive into the deeper path above.

T

About Tom Hundley

Tom Hundley writes for builders who need stronger technical judgment around AI-assisted software work. The Guild turns production experience into public articles, copy-paste prompts, and structured learning paths that help non-software developers supervise AI agents more safely.

Do this next

Leave this article with one concrete move. Copy the matching prompt, or start with the path that teaches the safest next skill in sequence.