I often like to think about technology projects from the perspective of their problem surfaces.
Assume every project has a surface: everything that it depends on. These can be diverse. They can be the development tools used to implement the project, libraries used inside the finished product, or online services it uses (including during development, or when it’s being executed). Some would call it the project’s stack.
Some projects have a very small surface: they might just be a small script written in some well known language with a well established execution environment. Or they might have a very large surface, if they’re a monstrous project of many dependencies, requiring an orchestration between many services and platforms for it to work properly.
Extending the problem surface
When adding new dependencies to a project, the way I see it, you’re not just extending its surface, but also its problem surface.
A project that depends on a small script written in a single language won’t have a very large problem surface. But it will still exist. Maybe some of the language features will get deprecated over time, in which case you’ll either continue depending on a specific version of it to run, or have to update the source code to match the new version. Or maybe the entire computer industry will migrate to a new processor architecture or operating system that doesn’t support the language’s runtime or compiler. No matter how unlikely some of those are, these are possible problems.
Every time you need a new dependency (in tooling, services, code, etc) to a project, you expand that problem surface.
Suppose that you want to add a tool to that small script infrastructure: you want to automatically check for errors in the code ahead of time, maybe by doing some sort of static analysis. This is a commendable feature that might help you produce a better product faster, since you will be catching errors earlier. It might be so well integrated with your development workflow that it’s invisible, giving you the impression that it’s problem-free. On the other hand, by simply adding a new tool to your workflow, you’ve still expanded the problem surface of the solution: you don’t just depend on that language, you depend on the static analysis tool. In the future, will it still be compatible with the language? Will its configuration introduce breaking changes? Who will spend time fixing those when the time arrives?
I am not against adding dependencies to a project. This is not a black-and-white matter. We can’t avoid expanding a project’s surface forever under the guise of maintaining a small problem surface. We just need to make sure that the benefits (in saved time, or productivity) are worth the investment (in maintenance and troubleshooting time) and the added risk (some dependency-related problems can have catastrophic outcomes). You can’t say a project with just one dependency is better than a different project with 5 dependencies either; not all dependencies are alike.
Still, in general, I tend to see extended problem surfaces as important red flags in the health of a project. They make me uneasy.
If you’re solving a project problem by, say, introducing a new tool, it’s always worth paying close attention to the headaches that tool will bring you in the future. Is the tool well maintained? Who is behind it? Is it well documented? Does it need extensive documentation? What kind of problems will happen if it starts misbehaving? How many people in your team, or anywhere else, know how to maintain the tool? How much time will this tool save, and how much will it require in return? Is there any other alternative when trying to solve that problem? These are important questions to be considered.
Remember adding dependencies to a product has negative cost (as they usually start by solving a problem), but removing them usually has a very high cost. It’s rarely ever done in practice, even when everybody knows that one dependency in the project is the one that causes the majority of the headaches. Without careful thought, being too liberal about extending the problem surface of your product can only ensure you’ll become a great case study on sunk cost fallacy.