Thinking about Dependency Management

The last 8 years can be characterized as a massive change in how we work with dependencies within PHP projects. The arrival and further advancement of Composer has brought a big change in how we decide which dependencies to use, how to maintain them and how we update and upgrade them. Only few people would deem this change as not positive – overall this has been a big success story for the PHP community and environment.

Unfortunately, often each blessing comes with strings attached, and this is no exception. Because it is so easy to add another dependency, we tend to add just another one without thinking about whether we really need it or if there isn’t another solution available. You may not maintain the library that you just added as a dependency, but the moment you add it it becomes your liability. Nowadays, it’s often not simply adding a single library – those have dependencies as well, and become part of your project and your liability as well. It may not be as bad as in the JavaScript world with npm (1, 2), but are we always really sure we want to have all those dependencies?

While we all enjoyed the new freedom made possible with Composer and earned the fruits, I think we didn’t look into the other non-positive aspects as much as we should. Are you sure you know about all packages that are in your application? Not just the direct ones you declared as a dependency, but the dependencies of those, and the dependencies of the dependencies of your dependencies? You could prolong this chain even more I guess.

The issue here is trust. For example, if you decide to add a package from Symfony, you place trust in the maintainers of this package. For a package from Symfony, that may be easy to do. But does this apply to all the maintainers of all the packages you use? At least for making sure your dependencies don’t contain known security vulnerabilities there are tools available, like SensioLabs Security Checker and GitHub Security Alerts. But what about packages where it’s not that easy to put trust into them? Theoretically, on each update you’d have to vet the new version to ensure it’s still ok to use for you.

Packages vary in size – for small packages of just a couple files it may be worth considering not to add the package as a dependency, but to simply copy the required code you just vetted (with respect to the license, of course!) to your application and not bother with thinking about the dependency. This might be easier said then done, as this has a tradeoff: you lose simple access to bugfixes and more importantly security fixes. However, the component might not be very susceptible to security flaws. The additional burden of maintaining this piece of code yourself might be worth it in constrast to the burden of caring about the dependency throughout the future.

On the other hand, adding it as a dependency will provide you with easy access to bugfixes and security fixes. But are you sure your dependencies are really updated often enough? Too often I’ve seen cases where developers don’t update and applications fall behind. Just because it might be easy to update it’s not done automatically. This might have reasons like missing unit tests so you can’t be sure that an update doesn’t break anything, even if it’s supposed to be a bugfix update only. To ensure upgrades happen often the existence of tests is necessary, but not sufficient. I believe dependency updates must be automated, and luckily we see the first steps in that direction, like automated security updates from GitHub. This is great, but I think we have the necessary ingredients in place to automate all bugfix- and probably minor updates this way, and we definitely need this as the amount of software just grows bigger and bigger.

As always in software, there isn’t a definitive answer, and decisions taken vary from situation to situation. But being aware about what adding an additional dependency (or more than one if this one itself has additional dependencies) entails is definitely not a bad thing.