Open package.json and count the lines. Now open your lockfile (pnpm-lock.yaml, package-lock.json
or whichever one your project has) and count those. The second number is usually somewhere between
three hundred and a thousand, and it is the real answer to "how much code is in my project".
Almost none of it is yours. All of it runs under your name, on your server, against your users' data.
The idea
A A package of code written by someone else that your project installs and runs. Your dependencies have their own dependencies, which is why a handful of packages becomes hundreds. is code you have agreed to run without reading. That is a good trade, because writing your own date library is a worse use of your life. The cost of the trade is that those packages keep changing: bugs get fixed, security holes get found and patched, and occasionally the authors decide to break something on purpose. Updating is how you stay on the version that has the fix. Not updating is a decision too, it just does not feel like one.
4 . 2 . 7 the version number, three parts
| | |
| | +-- PATCH bug and security fixes only
| | 4.2.7 -> 4.2.8 read the diff, ship it
| |
| +-------- MINOR new features, your code still works
| 4.2.7 -> 4.3.0 usually safe, still test
|
+-------------- MAJOR they removed or changed something
4.2.7 -> 5.0.0 read their upgrade guide firstHow it works
That three-part scheme is called semantic versioning, and most of the JavaScript world follows it. It is a promise, not a guarantee. A patch release can still break you.
Security updates are the ones with a deadline. When a vulnerability is found in a package, it gets a
public advisory with an identifier and a severity, and that publication tells attackers exactly what
to look for. npm audit (or pnpm audit) reads your lockfile and lists any advisories affecting what
you have installed. GitHub's Dependabot does the same thing continuously and opens a pull request
with the fix already applied.
Severity is about the package, not about you. A high severity flaw in a build tool that never runs in production is less urgent than a moderate one in the library that parses incoming requests. Read what the advisory actually describes before you panic or before you ignore it.
What to do
-
Turn on Dependabot security updates in your GitHub repository settings. It is two clicks, and it is the only part of this that starts without you. It opens a pull request; it does not merge it and it does not deploy. Nothing is patched until you merge and ship. Put a recurring reminder somewhere you will see it, because eleven unmerged Dependabot pull requests protect nobody.
-
Do patch and minor updates in a batch, on a schedule you can keep. Once a month is plenty for a small project. Commit everything you have first, so the upgrade is the only thing in the diff and you can undo it in one move. Then find out what is actually behind before changing anything:
codepnpm outdated --compatible pnpm update pnpm outdated --compatibleOn npm the equivalent is plain
npm outdated, where you read the Wanted column rather than Latest; there is no--compatibleflag and npm ignores it with a warning.--compatibleis what makes this diagnostic mean anything in pnpm: plainpnpm outdatedcompares against each package's latest published version, so it keeps listing packages that sit outside the range in yourpackage.jsonhowever many times you update, which looks like failure when nothing is wrong. With the flag, it lists only whatpnpm updatecan actually move. If the third command still shows the same packages, something genuinely failed to move, usually an override holding it down or a peer dependency refusing the newer version. Upgrade those by name, one at a time, and read what the resolver objects to. Then run the app the way a user would, including the paths that take payment or send email. -
Do major upgrades one package at a time, in their own commit. Ask Claude Code to read the upgrade guide for that specific version jump, apply it, and explain each change. Then read the diff yourself. A migration an AI performed and you approved without reading is a change nobody has reviewed.
Where it breaks
Updates break working software. This is the honest tension: the safest version to run is the one you have tested, and the safest version to run is the one with the security fix, and those are different versions. Nobody resolves that for you. What makes it survivable is being able to undo, which is why the commit comes before the upgrade and why an upgrade never travels in the same commit as a feature.
Automated tools also over-report. Advisories fire on packages that only run during your build, on transitive dependencies you cannot upgrade directly, and on flaws that need conditions your app never creates. If you treat every alert as an emergency you will stop reading them within a month.
The opposite failure is slower and much worse. A project two years behind cannot take a security patch without also taking six major versions of everything, and at that point the upgrade is a rewrite.