Mind your dependencies
Dependencies are often treated lightly, and few ever consider the risks introduced to the project with every new library you may want to use.
Do you need a lib for it?
According to studies, over 90% of the code in our projects comes from libraries.
The upside is that this removes from you the burden of implementing big, complex systems because someone has already done that before you. On the other hand, 90% of the time you have no idea what’s happening inside your project during runtime, so you lack control over things you are liable for.
Of course, things aren’t as scary as I am putting them here, but I still believe that we should be aware of what we are dealing with.
Obviously, there is no point in us reinventing the wheel, so we depend on dependencies. Still, we should take some precautions. We’ve summed up some things you might want to consider when you need to integrate someone else’s work into yours:
Think of the value the new library brings to your project.
Discuss with your team the problem you are trying to solve and consider and compare alternative solutions. Decide if it’s a viable solution to bring a new tool for the job, or if you’d get by with an in-house solution.
In bigger projects, it is often a problem when similar problems have already been resolved by other project contributors and a fitting library is already installed. Check your project’s codebase for similar problems. You may be surprised and might even save some time.
Assess the library’s credit of trust.
When considering a new tool, study the community’s opinion on it. It should give you an impression of how well it does its job, but also what new issues and overhead it might introduce.
Check the library’s issue tracker, see how well it is supported, and observe the communication between issue reporters and code maintainers. You’ll want to avoid solutions that have an unpredictable or stagnant release cycle, unaddressed questions in issue threads, tiny communities lacking experience in their niche, documentation composed of a few usage examples, and an undocumented API.
The dangers of Open Source
While open-source sounds like a great idea to many enthusiasts because it is maintained by the entire community, the reality is that this ecosystem is very vulnerable to the mistakes of lone individuals (an infamous example).
Depending on open source software means you lose control over a critical chunk of your project, and, in the most optimistic case, it means you might get a hard-to-catch bug. In the worst case scenario, you’ll introduce a security vulnerability and your entire production infrastructure will fall prey.
It is a misconception that open source is safe because everyone can review the code and projects are being maintained by groups of individuals with no commercial interest in the project’s path. Reality is different. Usually, most libraries in your projects are rarely maintained (during free time) by the solo efforts of individuals who have just decided to publish their personal tools. And these libraries, in their turn, depend on other libraries, again with variable levels of quality.
Even major projects are subject to these threats.
But it was working yesterday!
Another thing to consider when you decide to use a library is how well it controls its dependencies’ versioning. As it was mentioned before, libraries usually depend on other libraries, and that might become an issue if they do not manage their versions well.
When you install a new library, dependency managers in most programming languages follow the SEMVER versioning pattern, which allows flexible version ranges for your libraries. Many small libraries use ranged versions of dependencies rather than fixed versions. This, potentially, creates an avalanche of bugs in case just one of the dependencies in that tree fails after a minor patch. This kind of situation has occurred multiple times throughout the open-source software development history.
This creates the situation where your project may fail with no obvious reason the next time you clone it from the repo and sync the dependencies.
Most dependency managers create a lock file when you install a library. Many of us ignore it, some of us even .gitignore it, but let’s make it clear why it needs to exist in your repo.
When you install your libraries, your dependency manager resolves all the shared libraries, builds a dependency tree, and finds compatible dependencies to download and store. This information contains fixed versions, which should work on every machine if it works on yours.
Why do we need this? When the time comes and we have to publish our efforts to production, usually a Continuous Integration system takes over our code, clones it, syncs the dependencies, builds everything, and… fails with some version mismatch. You’ll be lucky if it fails during build time and not in runtime.
The lock file allows the CI to install exactly the same versions you had in working condition on your machine.