tech, developers, and the code underneath

issue 115· essay·

The dependency budget

Treat added dependencies like added latency: a number you spend deliberately, with a ceiling nobody may exceed quietly.

Nobody decides to have two thousand dependencies. It happens the way debt happens: one reasonable decision at a time, each locally correct.

The fix is not "have fewer dependencies," which is advice without a mechanism. The fix is a budget.

the model#

Treat total dependency count the way you treat page weight or p99 latency: a number with a ceiling, visible in CI, that requires a conversation to exceed.

yaml
# .dependency-budget.yml
production:  max: 180   # current: 164
development: max: 400   # current: 371

A pull request that pushes you over fails the build with a message asking for justification. Not blocking — a human can override — but the override is visible and requires a sentence.

That single mechanism changes behavior, because the cost of adding a dependency becomes visible at the moment of adding it rather than diffuse and unattributed.

what a dependency actually costs#

Make the list explicit so the conversation has content:

Security surface. Every package is code that runs with your privileges, from a person you do not know, that can change without your involvement.

Upgrade tax. Every dependency eventually has a breaking change, a vulnerability, or an abandonment. Multiply by the number you have.

Build time. Install, resolve, compile, bundle. Small per package, real in aggregate, paid on every CI run forever.

Cognitive load. A new engineer must learn each library's idioms. Twelve ways of doing HTTP requests in one codebase is a real onboarding cost.

Bundle size, if you ship to browsers, where it converts directly into user latency.

Transitive risk. You did not choose most of them. The one package you evaluated brought forty you did not.

the decision framework#

For any new dependency:

Would this be under 100 lines to write yourself? Then write it. left-pad was the canonical example and the lesson keeps needing to be relearned. A small utility you own is better than a small utility you rent.

Is it maintained? Last release, open issue count, number of active maintainers, whether the maintainer has responded to anything in six months. One maintainer is a risk. Zero is a fork you have not made yet.

How many transitive dependencies does it bring? A package with four dependencies that each have six is forty packages, not one. Check before you install, not after.

Is there a standard library equivalent? Increasingly there is. Node has a test runner, a SQLite driver, fetch, and .env parsing. Python's standard library is enormous and underused. Check first.

What happens if it is abandoned? If the answer is "we fork it and maintain it," that is fine and you should know you are signing up for it. If the answer is "we would be stuck," that is a much bigger commitment than it looks.

the tiering that makes this practical#

Not all dependencies deserve the same scrutiny. Three tiers:

Foundational. Your framework, your database driver, your runtime. Few of them, deeply integrated, expensive to change. Evaluate hard, once, and commit.

Utility. Date handling, validation, HTTP. Replaceable with a day of work. Moderate scrutiny. Prefer ones with few transitive dependencies.

Convenience. Saves you twenty lines. Highest scrutiny, because the cost is identical to a foundational dependency and the benefit is twenty lines.

The counterintuitive part: the smallest dependencies deserve the most scrutiny, because their benefit is smallest and their risk is the same.

the counter-argument, fairly#

Writing it yourself is not free. Your date handling will have bugs the mature library fixed in 2016. Your crypto will be wrong. Your parser will not handle the edge case.

The rule that resolves this: write it yourself when the domain is simple and you understand it completely. Use a library when the domain is deep.

Left-padding a string: simple, understood, write it. Time zones: deep, use the library. Parsing HTML: deep, use the library. Formatting a number as currency: looks simple, is deep, use the library.

the audit that is worth an afternoon#

Once a quarter, run your dependency list and ask, for each direct dependency:

  1. Do we still use this?
  2. When did we last update it?
  3. Is it still maintained?
  4. Could we drop it now?

You will delete some. The number goes down. That never happens by itself.

get README in your inbox

One dispatch, no noise. Tech and developer news, plus the occasional long piece on the craft.

subscribe →