Team Lead Mindset
Why This Package? Choosing and Carrying Dependencies
Six questions before you add a dependency, npm commands to check health, how to plan the exit, and a short decision-note template.
On this page
In one line: adding a package is borrowing someone else's code and promising to keep up with it. A good answer to "why this package?" shows that you checked the need, the health of the project, the fit with your stack, the cost, and the way out.
"Why this package?" is the most common "why" in a code review. The honest answer is never "it was the first result". This lesson gives you the six questions to ask before you add a dependency, the commands to check them, a short decision-note template, and model answers.
Start with Lesson 0 if you have not seen the six-step answer shape. For npm audit and lockfiles in a pipeline see Deployment and CI/CD, Full-Stack Lesson 26. For the popular libraries themselves see Popular Libraries, Full-Stack Lesson 13.
Everything here is common practice and opinion, not law. Teams differ. Use the ideas, not the exact words, and always use your own true examples.
What I checked. I read directly (raw text on GitHub): the npm command pages (npm view, npm explain, npm audit, npm outdated) from npm/cli, the OpenSSF Scorecard checks list, the Semantic Versioning text, and the architecture-decision-record repository (about 13k stars in the snapshot I found). I did not run the npm commands on a real project, because this environment cannot reach the npm registry. The "six questions" below are my own way of grouping what these sources check; they are not a standard.
Words used in this lesson
- A dependency is code written by someone else that your project needs, like an npm package. A direct dependency is one you added. A transitive dependency is one your dependency added for you.
- Maintenance is the work to keep something working after the first release: updates, fixes, security patches.
- Bus factor is how many people can leave (or be sick) before the project is in trouble. A bus factor of one is a risk.
- A decision record (ADR, Architecture Decision Record) is a short note that says what was decided, why, and what it costs. It helps people who join later.
"Why this package?"
Adding a package is borrowing someone else's code and promising to keep up with it. Before you add one, ask these questions. A lead is asking the same ones in their head.
1. Do I need it at all?
Can the platform do it already (fetch, URL, structuredClone, Intl, crypto.randomUUID)? Can I write it in 20 clear lines and test it? Small helpers are often cheaper to own than a dependency. Big, hard problems (dates across time zones, parsing, cryptography, forms with many rules) are usually worth a library, because the edge cases are many and you do not want to find them one by one.
2. Is it alive and cared for? Look at the last release date, open issues that nobody answers, how many people maintain it, and whether the project has a clear way to report security problems. A package with one tired maintainer and no release for three years is a risk even if it has many downloads. Check with commands you can run yourself:
npm view some-package time.modified # when did the package change last? (dot notation reads one field)npm view some-package license # which license?npm view some-package dependencies # what else does it pull in?npm audit # known vulnerabilities in what you installednpm audit signatures # verify registry signatures (and provenance, if there is any)npm outdated # Current vs Wanted vs Latestnpm explain some-package # why is this installed? who needs it?What these do, from npm's own command pages:
npm view <package> <field>reads one field of the package's registry record. Nested fields use dot notation (repository.url). Quote a field with special characters, such as"dist-tags.latest".npm auditsends a description of your dependencies to the registry and returns a report of known vulnerabilities. It exits with0when none are found, and non-zero otherwise.--audit-levelsets the lowest severity that makes it fail, which is how you use it in CI.npm audit fixapplies fixes that npm calculates.--forcealso allows major version updates of top-level dependencies, which can break your code, so read the diff before you accept it.npm audit signatureschecks the registry signatures of what you downloaded, and the provenance attestations when they exist.npm outdatedshows three versions. Wanted is the highest version that fits the range in yourpackage.json. Latest is the version taggedlatestin the registry, which may be outside your range.npm explainshows the chain of dependencies that causes a package to be installed. It is the best answer to "why is this in our app?".
These commands show facts, not a verdict. A recent release does not mean good code, and an old package can be finished and stable. Use the facts to ask better questions.
A ready-made health checklist: OpenSSF Scorecard
The OpenSSF Scorecard project (open source, on GitHub) automatically checks open-source repositories for risky habits. Its list of checks is a good model of what "is it alive and cared for?" can mean. These checks are the ones most useful for choosing a package:
| Scorecard check | The question behind it |
|---|---|
| Maintained | Does the project have recent commit and issue activity? |
| Security-Policy | Is there a SECURITY.md that says how to report a vulnerability? |
| Vulnerabilities | Are there open, unfixed known vulnerabilities in the code or its dependencies? |
| Code-Review | Were recent changes reviewed by a person before they were merged? |
| Contributors | Do recent contributors come from more than one organization? (a bus-factor signal) |
| License | Is there a recognised license file? |
| Dependency-Update-Tool | Is Dependabot or Renovate turned on? |
| Pinned-Dependencies | Are build and release dependencies pinned to exact versions or hashes? |
| Signed-Releases | Do releases come with signatures or provenance files? |
| Branch-Protection | Do the main branches require reviews and checks, and block force pushes? |
You do not need a score to ask these questions. Open the repository and look. Treat a score as one signal, never as proof.
Version numbers tell you the risk of an update
Most npm packages follow Semantic Versioning: MAJOR goes up for incompatible changes, MINOR for new features that stay compatible, PATCH for compatible bug fixes. A version that starts with 0. (0.y.z) means initial development: anything may change at any time. So a 0.x package is a bigger bet than a 3.x one, and a major bump is a task to plan, not a click to accept.
3. Is it a good fit for our stack? Does it have TypeScript types? Does it work with our framework version and our runtime (Node version, browser, edge)? Does it work with Server Components, or does it need the browser? A package that fights your stack costs more each week.
4. What does it cost to carry? Bundle size for browser code, install time, how many transitive dependencies it brings, and the license (can we legally use it in our product?). Every extra dependency is one more place for a security problem or a breaking change.
5. How hard is it to leave? If this package dies, how many files must change? A good habit: wrap it in one small module of your own, so the rest of the code calls your function and not the package everywhere. Then replacing it is a one-file job.
6. Who on the team can support it? If only you understand it, the bus factor is one. Pick things that more than one person can read, or write a short note on how it is used.
A simple rule many teams use: add a dependency only when it saves more time than it will cost over the life of the project, including the day it breaks. Say that number out loud, even if it is a rough guess.
A short decision note for a new package can be just this (a mini version of a decision record):
# Use date-fns for date arithmeticStatus: acceptedContext: We add months and days in the billing screen. Native Date makes thiseasy to get wrong near month ends.Decision: We will use date-fns, imported per function, in `lib/dates.ts` only.Consequences: Easier: correct month maths, typed API. Harder: one moredependency to update. We can swap it by editing one file.This follows the usual shape of a decision record (title, status, context, decision, consequences) that Michael Nygard described. The architecture-decision-record repository lists many other templates (MADR, arc42, Y-statements and more) and gives practical advice:
- Write one when you want future developers to understand the "why". Skip it when the decision is tiny, limited in scope, time, risk or cost, made by one developer, already covered by standards, or temporary (a workaround or a proof of concept).
- Date every record, especially anything that can change, such as costs.
- Do not rewrite history. Amend the record with new information, or write a new one that replaces it.
- Look back. The repository suggests reviewing a decision about a month later and at least once a year, to compare it with what really happened.
The point is not the paperwork. The point is that the "why" is saved for the next person, who will otherwise ask the same question again.
Interview questions
Why did you choose this package? (Must know)
I start with the need: "We had to do X." Then I say what I compared: the built-in way, two or three packages, and writing it ourselves. I give two or three facts for my choice: it is maintained (recent releases), it fits our stack (TypeScript types, works with our framework version), it is small, the license is fine, and few extra dependencies come with it. Then I name the risk and the exit: "If it dies, it is used in one file, so we replace it there." I do not say "everyone uses it" as my only reason.
Follow-up: "What if the maintainer stops working on it?" I check how widely it is used and whether there is a fork. I keep the package behind my own small wrapper, pin the version, and I watch security advisories. If it stays important, we can replace it or take over the small part we need.
When would you write something yourself instead of adding a package? (Must know)
When the need is small and clear, the platform can almost do it, and I can test it well. For example, a 15-line helper for grouping an array. I do not write my own for hard problems with many edge cases, such as dates and time zones, cryptography, parsing, or sanitising HTML. There, a well-tested library is safer than my version. The question I ask is: "Which one costs less over the life of the project, including the day it breaks?"
How do you check that a package is safe to use? (Senior signal)
I look at several things, not one. Maintenance: last release date, how fast issues are answered, number of maintainers. Security: npm audit, known advisories, and whether the project has a way to report problems. Size: how many transitive dependencies it brings. License: can we use it in our product? Trust: is the name exactly right? (Fake packages with names close to real ones exist.) I install with the lockfile committed and use a clean install in CI so that versions do not change without review. I also avoid packages that run install scripts without a good reason.
Key takeaways
- Ask first: do I need a package at all? The platform or 20 clear lines may be enough.
- Hard problems with many edge cases (dates, parsing, cryptography, sanitising HTML) are worth a good library.
- Check health: last release, unanswered issues, number of maintainers, security reporting.
- Check fit: TypeScript types, framework and runtime support, Server Components.
- Check cost: bundle size, transitive dependencies, license, install scripts.
- Plan the exit: wrap the package in one small module so replacing it is a one-file job.
- Write a short decision note: title, status, context, decision, consequences.
- This is common practice and opinion. Adapt it to your team.
What's next
Next: Why this language or framework? Lesson 2. Practise: run npm explain and npm outdated on a project you know and answer "why is this here?" for five packages.
Published by Jainik · Last updated · How these lessons are made
Keep learning
Related lessons
- Dependencies and the Supply Chain: npm, Lockfiles and GitHub Actions · Security for Developers
Part of the study guide
Related series