On the Rather Delicate Art of Choosing Dependencies

Here's what I tend to look out for when I'm sizing up a new dependency. Not rules, mind you—just patterns I've noticed after years of occasionally getting it wrong.

  • Popularity and adoption (or: choose boring technology): There's something to be said for a library with a few thousand stars rather than twelve. Popular libraries tend to have sorted out the edge cases already. More importantly, I tend to favour what some call "boring technology"—proven, stable things that have been around for a while. You've only got so many innovation tokens to spend on a project, and each shiny new dependency spends one. The long-term costs of keeping systems working reliably vastly exceed any inconveniences whilst building. Boring doesn't mean dead—it means the unknowns are known, and that's rather valuable.

  • Activity and maintenance: I always check when the last commit was. If it was three years ago, that's either brilliant or rather concerning, and it's usually the latter. I look for projects that show signs of life—not necessarily daily commits, but evidence that someone's still home and answering the door.

  • The maturity paradox: Here's an interesting wrinkle—sometimes low activity is actually a good sign. There's something called the Lindy Effect: the longer something's been around, the longer it's likely to stick around. Mature software becomes more stable, and stable software needs fewer changes. Think of Unix command-line tools or SQL—they've been around for decades precisely because they're solid. That said, this seems to apply more to foundational technologies than to libraries in rapidly-evolving ecosystems like web development. A framework that hasn't changed in three years might just be genuinely stable, or it might be quietly gathering dust. Context matters rather a lot.

  • Documentation quality: Good documentation is worth its weight in gold. I have a look at whether the docs actually help me understand how to use the thing—quick-start guides, examples, complete API references. Poor documentation means you're signing up for a lot of source code archaeology. Equally important: the docs should be up to date and properly versioned. Nothing's quite as maddening as following a tutorial only to discover it's for version 2.x and you're on 4.x.

  • Dependency tree: I try to have a look at what I'm bringing in. You want to add one small library, but it depends on seventeen others, and before you know it your node_modules folder has its own postcode. Every dependency is a potential vulnerability, so it's worth understanding the blast radius.

  • Licensing: I always check the licence before I commit to anything. Most of the time MIT or Apache 2.0 are fine, but occasionally you'll run into GPL or some homegrown licence. It's not fun, but neither is explaining to legal why half your codebase is now subject to copyleft provisions.

  • Upgrade path: I look at the project's versioning history. Do they follow semantic versioning? Do they provide migration guides for breaking changes? Libraries that make upgrading difficult become "frozen dependencies"—fine until a security vulnerability turns up.

  • Bus factor: What happens if the maintainer gets hit by a bus or just gets bored? One-person projects can be brilliant, but there is inherent risk. I tend to look for projects with multiple contributors or at least some kind of succession plan.

  • Problem fit: Does this actually solve my problem, or does it solve something adjacent whilst introducing three new problems? I've definitely been guilty of trying to shoehorn in a clever library. Sometimes the boring solution is better than the clever, dependency-laden one.

  • Learning curve: Every new dependency is something else to learn. I look at how steep the learning curve is—can I get something working in an afternoon, or will I need a 400-page manual? I try to match the complexity of the tool to the complexity of the problem.

  • Vendor lock-in: Once you're in, how hard is it to get out? Frameworks that play nicely with standards and don't require you to structure your entire application around their opinions tend to age better. The ones that want you to do everything their way can be brilliant right up until they're not.

The Maintenance Reality

Choosing a dependency isn't just a technical decision you make once—it's signing up for an ongoing relationship. That library will need updating, might break, might conflict with others. The dependencies you choose today become part of your maintenance burden tomorrow. The best dependencies minimise that burden by being stable, well-maintained, well-documented, and playing nicely with others.

And when in doubt, remember: the best dependency is sometimes no dependency at all.