Every dependency is a loan, and the interest is paid in bytes and risk
Adding a dependency feels free — one npm install and the problem is solved. But a
dependency is a loan, and the principal (the feature you got) is the small part.
The interest is paid later and forever: bytes shipped to every user, a larger security
surface, a maintenance burden when it needs upgrading, and the day it breaks, is
abandoned, or ships a change you did not want. None of that shows at install time,
which is exactly why the decision gets made carelessly. Borrowing is often the right
call — you should not reimplement a date library — but it is a call, and it should be
made by weighing the interest before you sign, not discovering it after.
The interest is mostly invisible at install
Weigh what a package actually costs before adding it. Bytes: it ships to every user, on every load, forever — a 40KB library for a 10-line need is a bad trade. Security: every dependency (and every dependency of that dependency) is attack surface and a future CVE to patch. Maintenance: it must be upgraded, and majors break. Breakage: it can be abandoned or hijacked. Before installing, do the napkin check:
# what am I really borrowing?
npx bundlephobia left-pad # bytes added, min+gzip, to every user
npm view some-lib dependencies # its transitive tree — each one is more of the same
# then ask: is the feature worth THAT, or can I write the 10 lines?
Sometimes the ten lines are the cheaper loan
A lot of tiny dependencies exist to save code you could write and own in minutes — and owning it removes all the interest. A debounce is the canonical example: a one-line reason to reach for a utility library, and a five-line reason not to:
// this is the whole "dependency" — writing it costs less than auditing a package for it
function debounce(fn, wait) {
let t;
return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), wait); };
}
The rule is not “never depend” — it is “don’t borrow what is cheaper to own.” For
debounce, deepClone of simple data, or a small formatter, the code you write has
no bytes-tax, no CVE, and no upgrade day.
Borrow deliberately, and put a budget on it
For the genuinely hard, well-solved problems — dates, virtualization, a rich editor, crypto — do borrow, because reimplementing them badly is far more expensive than the interest. The discipline is to make it a decision: prefer a dependency that is small, focused, actively maintained, and tree-shakeable over a kitchen-sink library where you use 5%. Then enforce the aggregate with a bundle budget that fails the build when the total crosses a line, so “one more package” cannot quietly balloon the app one careless install at a time:
$ node harness/bundle-budget.js
FAIL entry chunk 268kb > 250kb budget (moment 19kb, lodash 24kb — both replaceable)
That number turns “every dependency is a loan” from a proverb into a check. Borrow on purpose, own what is cheap to own, and keep a budget on the total — the harness-bundle-budget exercise builds exactly that gate, and debounce-utility and deep-clone are the “own it instead” cases in miniature.