Your Dependencies Are Not Free
Dependencies are free chunks of functionality, except when they're not. They come with hidden costs which need careful consideration.
At first glance, depending on a library to fill a requirement is just good engineering. DRY, probably the best-known software engineering principle, tells us to avoid duplication like the plague. And what is writing your own code over grabbing an off-the-shelf package, if not duplication? In most programming languages, the majority of available libraries are free and open source, so it's not like we're paying for the privilege.
Dependent on Problems
Alas, libraries are not created equal. For every package that is consistently updated, maintained, patched, and released β all with good hygiene and tidy upgrade paths β there are many more with security vulnerabilities, subpar performance, incompatibilities with other dependencies, regularly undocumented breaking changes, and abandoned status.
It's easy to write and release a library, but not all authors retain long-term interest to keep it in good standing. I myself have written packages to support single projects, then moved onto other projects, and since not had the time or interest to continue maintenance.

Public packages are usually built with generalisation in mind, such that they can be applied to various projects by way of configuration parameters. That means more code for your project to inherit, and more code means complexity, and more complexity means more things that can go wrong.
The best code is no code at all. Just because you don't write a piece of code doesn't mean it doesn't smell up your codebase.
I might, for example, need to paginate a list of database results β a common problem β so I look for a package and I've got it installed within a minute or two. Suddenly my codebase has exploded to include configuration management, pagination templates to suit every need, adapters to databases I've never heard of, and a dozen more dependencies that my dependency depends on!
In extreme cases, dependencies might even sabotage your work. Recent history has no shortage of supply chain attacks, demonstrating very real, very expensive consequences.
Like the left-pad incident, which halted build systems around the world and left shockwaves throughout the Node community, whose ecosystem grew large and interdependent due to the minimal standard library and massive popularity.

More recently, we saw the misguided peacenotwar malware, which was the software equivalent of protesting a war by shooting the perpetrator's civilians. This one was intently malicious, destroying files on the installer's machine.
And what about the sophisticated effort to (successfully) introduce a backdoor into xz? Just like in a big budget espionage film, the villain spent years crafting a persona to eventually deliver their payload. We're lucky that package maintainers caught it before it had a bigger impact, but it scared us all into taking supply chain security more seriously.
What's The Alternative?
So, am I saying that we should write every piece of code ourselves and ignore off-the-shelf packages? Of course not! Taken to its logical conclusion, this would mean you can't do anything without writing Assembly!
There is no realistic way to completely avoid dependencies, but we don't need them for every little thing. Just like you don't need to install that restaurant's iOS app for a one-time reservation, avoid throwing everything at your application just because it exists.
Copy That Floppy
One such alternative is copy-and-paste programming. Most developers, myself included, cringe at the thought of copy-and-pasting code, because it perfectly contradicts the DRY principle that was hammered into us. Surely any time we re-use code, it should be packaged into something for re-use!
But I'm seeing more instances where code is either copied or generated and becomes a first-class citizen of the project. This means that maintenance is down to us, the project owners, but the barrier of entry to make changes is considerably lower (consider how much work it can be to get a single change into a public library) and we don't have to write the thing from scratch.
Plus, the code only needs to work on your project, so we don't need to worry about configuration for other scenarios.
Tailwind ushered in a wave of copy-paste programming in the frontend, and Rails has always provided code generators, with one of the most recent being its authentication generator. You can just as well copy a method out of a third-party library (licence permitting, of course) and paste it directly into your project.
Write it Yourself
It can feel like building applications is a matter of gluing components together and adding business logic, but there's nothing wrong with writing whole new functionality yourself. It's totally fine, or even preferable if new code is laser-focused to solve a single problem in this single application.
Perhaps writing it yourself means reaching for features of the standard library β a good reason to know it well, or use as an opportunity to know it better β or even pulling in lower-level dependencies that do fit your criteria. In any case, you'll end up with a solution that is tailor-made to your problem, with none of the cruft that isn't. Less code, less maintenance, fewer problems.
But Mostly, Be Mindful
I hope that, by now, we agree that adding dependencies is not a decision that should be taken lightly. We all have our own experiences with existing libraries β what has worked before, what hasn't, which author tends to abandon their projects, etc. β but here are some general questions that can be helpful to ask when faced with such a decision.
- Is there already something in the codebase / current dependencies / standard library that can do this? β On long-running projects, ask a colleague with more tenure to ensure you don't miss anything.
- How much time would it take to write this myself?
- Do I have the skills to write this myself, or should I delegate it to an expert? β Always ask this when security is concerned.
- Can I take steps to make it easier to replace this later? β It can be wise to hide the behaviour behind a faΓ§ade.
- Is the package currently maintained?
- Is there an incentive for the maintainer to keep this maintained? β For example, software like Sidekiq have a paid upgrade, offering financial incentive.
- Does the package have a history of security issues, and if so, how quickly are they remediated?
It turns out that dependencies can be pretty pricey after all. Whether we're willing to accept the cost depends on numerous factors, but being aware of it is the first step to making the right decision.