Choosing Open Source Dependencies: this is how you do it
“Don’t reinvent the wheel” is the holy grail of coding advice. Building on the frontiers of technology is how we humans got as far as we did. If you need to get all your team on the same page, it’s almost always better to go for a ready-made solution than to build one from scratch. In the abundant age of open-source, there is no excuse to waste your time on tasks that are only secondary to your goal.
Herein, however, lies the devil. Github, almost a year ago, announced that it had hit the 100 million repositories mark. According to modulecounts.com, NPM currently has over a million packages, Apache Maven over 300,000, and Packagist around 240,000. Point is, we have many open-source solutions to fit your every need – almost too many.
So in the ocean of open (source) options, what dependencies should you pick, and how should you be picking them? Should you go for Angular, Vue or React? Flask or Django? Bootstrap or Bulma? Or rather a more difficult choice would be “should I use this seemingly awesome random package I just found?”
Of course, more options mean more low-quality ones. The shocking figures of available open-source software become much less shocking when you exclude all the non-production-quality ones. But hold on a minute – this is exactly the difficult part. Quickly you’ll notice how this process of sifting out bad packages changes from a pleasant browse through a department store to an intense game of minesweeper.
It’s tricky business; no matter what you do there will always be the looming threat of your decisions coming back to bite you. There is a lot to consider when making decisions about your dependencies. Generally, there are three things you should keep in mind: your project-dependency compatibility, repository “hygiene factors”, and the community/support of the library.
How to choose project-package compatibility
Perhaps the first thing that comes to mind when discussing what dependencies to use, is how well that dependency suits your purposes. It’s certain that you won’t be using every feature of your dependencies. Not only that, but you know you will always need more than what a single package provides and that you will have to patch that with even more dependencies.
It’s a good idea to start by carefully outlining what you need for your project. If you are only utilizing a fraction of what a library is capable of doing, then it would be better to opt for a different solution. Efficiency reigns supreme in software development. Your dependencies need to be, first and foremost, a function of your desired result.
Ask yourself a couple of questions: does this package cover your projects needs? Will it continue to meet your needs in the future? If the answer is no, then would you rather patch it up with another dependency, or are there packages more suited to your specific needs? If the planning phase seems excessive, just remember – or remind your developers – that every good decision you make now is one less pain to deal with throughout the project’s lifespan.
Speaking of efficiency, what use is the hypothetical best-library-out-there in the hands of a developer that can’t utilize it? Preference should come low on the list of things that decide your project, or else every programmer in your team would be working separately in their own environment.
However, your team’s skill set is akin to a plumber’s toolbox, and foreign technology is no better than a makeshift tool. While it could be, in certain instances, a good idea to keep up to date with newer (probably better) software, sometimes it is a better decision to stay on the more tried path.
It takes a lot of money and time to learn new software – money which would make your project unprofitable, and time which will get you in deep water with your client. It’s a fantastic idea to sit with your team and discuss what you can work with. Your team’s skill set is as important a resource as any other.
Lastly, as obvious as it seems, always make sure your dependencies are compatible. I can’t count how many hours I’ve spent scratching my head at a mysterious error only to discover (much) later that my dependencies are refusing to play nice. Wouldn’t it be nice if React worked just fine with vanilla Bootstrap?
Less obviously, but equally as frustrating, is which versions of your dependencies are compatible. Build automation and dependency management tools will often give you hints on what you can, can’t, should, and shouldn’t mix together. But if that’s not your thing, then a quick google will turn up a few stackoverflow threads by valiant souls who struggled with similar issues so you don’t have to.
Dependencies: hygiene factors
It is important while working on a project to occasionally pull out of the developer headspace and into that of a project manager. As cool as some software seems to be, you need to consider that bringing in a new dependency is a long-term (as long as your development period will be) commitment.
To that effect, there is an entire category of factors that you should keep in mind that have little to do with the technical parts of a package. The numbers that show up at the top and bottom of a GitHub repo can give you an idea of what to expect out of a package. Contributors, downloads, updates, and issues can give you very good insight on whether to commit – no pun intended – to using a package or not.
The first, and perhaps most obvious, “hygiene” factor to mind is the contributors to the piece of software you are eyeing. Not only should you be looking at how many people are working on the package, but also what those people have worked on before. It seems a bit overkill to be doing private investigations just to pick a dependency, but you have to remember that these small decisions end up affecting the quality and maintainability of your project.
After all, all you need is a glance at some of the contributors’ other (unrelated) projects. Does this contributor maintain continual support of their more serious projects? Does that one contribute to other well known libraries? You don’t need much to go on, just some assurance that your dependency won’t be abandoned two months after you adopt it.
Staying up to date when Choosing dependencies
The beauty and terror of being a developer is that last year’s industry standards could be obsolete by next year. The world of software development is fickle, changes are the norm. This is very exciting for enthusiasts who live off news and dazzling trade shows.
For the average developer, however, all this sounds like there is a lot of pain updating and continually supporting your software. Like any good developer, you have, of course, future-proofed your software; your project is scalable, your code is robust, and you throw out patches and fixes as soon as issues are found.
Even though you have done everything you could, you may be at risk of facing security issues or at least a mountain of frustrations. Why? Because the last time your dependency was updated was sometime last year.
It seems a bit unfair that something you had no hand in would cause you issues down the line, but in reality good planning is your responsibility. A sign of a good package is that it is frequently updated. When last did the developers of that package you are using commit to their software? Ask yourself what that means for your project right now, after it is completed, and in the more distant future.
While you are still inspecting the repo, take a quick look at the issues – well, the open ones. Again, if it breathes, it’s good. Issues are inevitable, and sometimes they could be difficult to solve. However, if the issues are left out to collect dust you may want to reconsider working with the package. If there is a significant number of relatively old issues that were never resolved, it could be that the developers are not really keen on keeping their project fresh.
All in all, it is crucial to consider whether a package is active and fresh. Is it maintained by a lone wolf or the community? How often are updates committed to the project? When was the last one? What about issues, are there many open issues? How long does it seem it takes contributors to respond to issues? You get the idea.
Other things that matter when you choose your dependencies
The potential-dependency is all clean and active, so what now? Should you start coding with the dependency already? Well, not quite. There are still things that will have you regretting your decisions if you do not carefully mind them.
If you still haven’t heard, Semantic Versioning is pretty much mandatory these days. Besides being just an overall good practice, Semantic Versioning is at its core a very good attempt at solving the issue we have at hand. Updating and using different versions of a library is the number one villain to dependency management. At a glance, Semantic Versioning tells you all you need to know about how to and if you should move from one version to another.
The first bit of the version number will give you a big heads up; if you’re on version 1.X.X and you want to move to version 2.X.X, the change is a big red flag telling you that you will get issues. Expect major changes that are not necessarily backward compatible. The second bit denotes addition of a feature, but nothing really disruptive. Finally, the last part just means a small patch that probably does not imply anything for you.
But what do versions mean if you can’t even figure out how to use the library? Good documentation goes a long way. Before committing to a library that has a sweet feature, make sure that it is at least well documented. A solid description and one or two code examples will save you time like nothing else. A good developer will probably be able to figure out how anything works by reading the source code. A great developer won’t bother trying.
Open-source is, well, open. The source code is available for all to see, including those with malicious intents. It is significantly easier to find exploits and security vulnerabilities in an open-source solution. Admittedly, it is a time-consuming task to figure out the vulnerabilities (or lack thereof) of any piece of software. It just isn’t realistic to analyze the security issues of each and every dependency you intend to use.
The next best thing you can do – if the library is well-known – is to just google relevant security issues. Vulnerability databases, most importantly National Vulnerability Database, disclose and manage information about vulnerabilities in many pieces of software. It is totally up to the developer to decide how to move forward in the face of uncertainty about security. Security is too big and too serious of an issue to ever get too comfortable with. Check out our post about security vulnerabilities, If you would like further knowledge on the subject.
The last, but far from the least, important factor to consider is licensing. As a developer, you may not be the most fluent in legal-language; unfortunately, that won’t stop anyone from suing you. Open-source does not mean that you get to do whatever with the code. Before you use any dependency (especially for a commercial project) check the license. If you always do that, good on you. If you don’t, please make it a habit for your own safety.
The good news is, the most common type of open-source license (MIT) allows you to do everything you want to do with the code as long as you leave the original copyright and license notice in any copy of your software.
This isn’t always the case, however. There are loads of other licenses and there are variations of each of them. Thankfully, the internet can make legal jargon understandable to anyone without a law degree. So, next time you’re eyeing a package pay at least a little bit of attention to its licensing.
Continual dependency management
Congrats, you have successfully chosen the dependencies you are going to be working with. It is a generally less exciting step, but it’s a part of the planning phase. Cautious planning is a good omen for any sort of project.
Even though you have taken your time with it, you might be feeling a bit uneasy. “Did I do it right?” you might be wondering. Most likely you did, it isn’t rocket science. The good news is, plans are not permanent. While it is ideal to have planned everything perfectly from the get-go, chances are you won’t get it perfectly right. Adding new dependencies, removing ones you’re already using, or replacing one with another are all far superior options to sticking with a faulty plan.
Evaluating decisions
Sometimes things just don’t work out the way they panned out in your head. The most obvious thing to look out for is lapses in your judgment. Maybe you realized too late the licensing on a specific package is not very permissive, or maybe you forgot to check and a dependency you are using turns out to be dead. It seems silly and too obvious, but planning is an ongoing process. It never hurts to be wary of silly issues, they can derail your workflow just as horribly as grave mistakes.
Another time you want to be on the lookout for issues is when you introduce new dependencies. Issues can arise between the old and the new if you are not careful, which puts you at a crossroad of decisions.
Your team’s productivity and satisfaction with the development environment can give you a heads up about how well you did choosing dependencies. The best technology is useless if your team is not efficient with it. If a dependency is causing too many issues, then surgical action is required; drop it. Whether it is bad documentation or inexperienced developers, the end result is what matters. It’s good to adopt new technology, but maybe take the time to learn it and test it out on less important projects.
Retracing dependencies
Speaking of dropping or changing dependencies, remember to take the same rules of picking dependencies and reuse them. Dependency issues, especially those that have to do with versioning, can arise every time you make changes to the structure of your project. If you’re substituting a particularly large dependency, make necessary adjustments in your code before subbing out the problematic package.
There is nothing that will cause mysterious issues down the line like legacy code that no one wants to touch because no one knows exactly what it does. As with anything, caution and common sense are your best friends.
Reaping the rewards
The effects of proper planning are far-reaching. The whole idea of planning your dependencies is taking out all the future work and headache and dealing with it first – before too much piles up on you. Sadly, you will neither see ridiculous improvement to productivity nor magically create the most robust code you’ve ever written. The effects are more subtle. If you’ve ever dealt with bad planning you would be able to appreciate not having to deal with it.
Instead of having to deal with a mid-project-life crisis where all hell breaks loose and schedules get derailed, you’ll be rewarded with a smooth workflow all the way through to the end – at least in regards to your dependencies.
Choosing good dependencies is just one of the few challenges developers face on a regular basis. However, it is criminally overlooked. Most of the more obvious issues have already been optimized by the general developer population. Optimizing strategic dependency choices just seems like the next logical step to create a silky smooth workflow. Saving the time you were going to spend on resolving dependency issues means creating time for more important – not to mention, less frustrating – issues.
Choosing the wrong open-source packages can be a make-it-or-break-it decision for your project; an informed decision is worth the time.
How Debricked can help you pick your dependencies
Debricked may not have all the answers to these problems. But we can help you with a few of them; namely security, licensing & the questions regarding the quality and health of the specific open source components that you use. Do not hesitate to get in touch with our team if you would like to know more!