Choosing a Third-Party Library
It’s been hours. You’ve been Googling and Googling, trying to figure out the best way to implement that slick JavaScript animation. Turns out, what seemed so simple and commonplace is not so simple after all. But what is this? A blue link appears in your Google results — one you haven’t visited yet. It’s a link to GitHub. There’s an npm package that does exactly what you need. yarn add somepackage
. You’re done right? BOOM.
Wait.
It’s a common scenario. Hours of frustration and confusion culminate in a single line in your package.json
, Gemfile
, or requirements.txt
. A thorny problem becomes a black box. What’s not to love? Turns out, there’s plenty not to love. In this post, I’m going to go over some of the considerations involved in choosing third-party libraries to use in your code. Although I’m primarily focussed on open source libraries, most of this post applies to proprietary third-party dependencies as well.
What Is a Third-Party Library?
When I say “third-party library”, I’m referring to any code written by someone else that you can import to use in your own project (using your chosen language’s import or require syntax). Typically, these take the form of npm
packages, pip
packages, Ruby gems, or whichever type of packages are used in your language ecosystem. Not every package is a library, however, much of the advice in this post holds whether a third-party package is technically a library or not.
Why Not?
So what could be so bad about this new, out-of-the-box React component that already does exactly what you’ve been researching for the last hour and a half? Surely it couldn’t be worse than spending who knows how much more time figuring out how to do what it does on your own, right?
Third-Party Dependencies Are a Responsibility
Third-party dependencies can be a powerful addition to your project. But you know what comes with great power: great responsibility. Every dependency you add to your project represents another thing to keep track of and keep up-to-date. How will you know when a new version comes out? How will you manage it if a new version introduces a dependency that conflicts with one of your application’s other dependencies?
Third-Party Dependencies Are a Risk
When you add a third-party dependency to your code base, you are making a bet that that code is secure, that it will continue to be maintained, and that it will continue to meet your needs and be compatible with your project in the future. Any of these things could turn out not to be the case.
Should You Use a Library At All?
Sometimes, it’s best to take the extra time and write your own code. There are some advantages to doing this:
- You have total control over the code and can make it do exactly what you need
- You have no new dependencies that you need to manage and keep up-to-date
- You don’t need to rely on third-party maintainers to add new features, test, or maintain the code
- You don’t need to wait for a new release to add functionality or fix security holes
Using a library does have its own advantages, though, and sometimes these win out:
- You have less application code to write, test, and maintain
- You’re not needlessly duplicating effort when something has already been done
When to Use a Library
There are five questions I usually ask myself when deciding whether to use a third-party library — any third-party library — to solve my problem:
- How long is my time horizon?
The shorter-term your project, the lower the risks of using a third-party solution. For example, if you’re writing a web site for a special event in 6 months, you might not care if the library stops being maintained after 6 months, or if future versions diverge from your needs. - What is the project?
If this is a side project or a proof-of-concept, concerns about future compatibility, maintenance, testing, documentation, and security may not be relevant. For a major client project or an app running in production for your employer, you might care more about those things. - Is this functionality part of my app’s core function?
If this is functionality your application will rely on to do any of the fundamental things it needs to do, consider taking the time to implement this yourself. If a product’s core functionality or a company’s competitive advantage rest on what you’re doing, it’s very risky to depend on a third party to provide what you need. - Do I, or does my team, have the skills to develop and maintain this functionality?
Sometimes, you’ll run into a problem you simply don’t have the skills to solve. If the functionality you’re implementing is not related to your app’s core function (see question 3), it may make sense to rely on a third-party library so you can focus on your core competencies. If the functionality is related to your app’s core function, consider developing your own or your team’s skills and doing it yourself. (Another option, if you’re in a hurry, could be to have a contractor implement the functionality and train your team.) - Is there an established way of implementing this functionality?
If you are solving a solved problem that just happens to take a lot of code, a third-party library that can be a great option — provided its implementation adheres to those existing best practices. If you can’t find one that’s well-maintained, another option in this situation is to write your own library using established patterns or best practices. - Is a suitable library available?
This is often the deciding factor — whether there is an existing library that does what you need it to and fits the library selection criteria outlined below. If no library meets the suggested selection criteria, my advice is to implement the functionality yourself.
To summarise, consider developing the functionality in-house and not using a library if:
- You’re working on a project that will need to run in production for a long period of time
- The functionality in question will be a core function of the application or a core competency of the business
- You or your team have or can reasonably develop the skills needed to implement the functionality well
- There is an established way to implement the functionality and you can’t find a suitable library that does it that way
- There is no existing library that both provides what you need and meets suggested criteria for third-party dependencies
Choosing a Library
Once you’ve determined that your use case calls for a third-party library, there are several factors to consider in determining whether a suitable library exists and, if more than one is available, which to choose. I recommend adopting strict criteria for all third-party dependencies and, if you can’t find one that meets them, implementing the functionality yourself.
Here are some of the factors to consider.
Maintainers and Contributors
When you adopt a third-party library, you are effectively inviting its maintainers onto your dev team. If that makes you uncomfortable, consider using a different tool or implementing the functionality yourself.
There are several things to think about when evaluating a project’s maintainers and contributors:
- How many maintainers are there?
The more maintainers are on the team, the more likely the project is to continue being maintained when one of them burns out or moves on to other projects. - Is the project sponsored, or are the maintainers being paid?
Most open source maintainers, even those working on large projects, do their open source work on their own time for free. That means that that open source work is the first thing they drop when they realise they don’t have time to spend with their families, need to take on more paid work, take up a hobby, or do anything else that conflicts. You won’t end up with very many open source libraries if you categorically reject any on this basis, but if you want assurances that the maintainers will continue to prioritise upkeep of this library, a library with paid maintainers is the safest bet. - Who are the maintainers?
Are the maintainers people you know or have heard of? Do they have technical blogs? Are their opinions on technical topics compatible with yours? Are they recognised experts on the library’s problem space and the technologies involved, or are they just people like you sharing solutions they’ve found to common problems? I avoid libraries written by regular devs sharing solutions they’ve found — while well-intended, my experience is that these maintainers are less responsive, less likely to consider the needs of those using their libraries, less likely to put time into maintaining the code base, and more likely to abandon the library as soon as they no longer have a need for it themselves. - Is there a large base of community contributors?
The more community contributors, the better. Maintainers, especially unpaid ones, only have so much time to devote to a project. A large base of contributors also tells you that the community is invested in the project, so if the current maintainers quit or abandon it, someone else is likely to step up and take it over.
Profile and Sponsorship
Similar to the question of paid maintainers is the question of project profile and sponsorship. High-profile projects and those underwritten by larger, better-funded organisations are the most reliable and most likely to stick around.
One quick litmus test in this area, especially for open-source projects, is whether the project has its own GitHub organisation or user. In other words, does this project live at https://github.com/coollibrary/coollibrary, or https://github.com/somerandomuser/coollibrary? Projects that have their own GitHub organisation are usually, though not always, better established and stick around longer.
Age and Version Number
In production projects with long time horizons, I recommend avoiding libraries that are recently developed (within the last 1–2 years) or still have a major version of 0. There are exceptions to this, such as when the library deals with integrating a technology that was introduced in the last year or two. Even in those cases though, keep in mind that a software project is easy to start and hard to keep going. Lots of maintainers will create a library with good intentions but not want to stick with it for the long haul.
Number of Downloads
A large number of downloads means a large community that depends on the library staying up-to-date and working right. A library with a small number of downloads is at risk for being abandoned by its maintainers when they get limited engagement from the community.
Maintainer Responsiveness
One of the most critical factors in making things work with a third-party library is how quickly maintainers respond to community needs, whether those be support for a new language version, patching security flaws, fixing bugs, or adding new features. In determining maintainer responsiveness, ask yourself the following questions:
- How frequent are releases? When was the last version released?
Some teams of maintainers release more often than others, so what you’re looking for is a regular cadence. If a library integrates with another technology, check how quickly it is updated after a new version of the other technology is released. For example, if you are evaluating theredis
Ruby gem, see how soon it was updated after the last version of Redis came out. - How long ago was the last commit on the main branch?
Imagine that you are waiting for a bug in the library to be fixed. Would you be able to wait that amount of time or more? How long was the last commit before that? Most actively maintained projects should have commits at least every few weeks. As a rule, I consider a project dead if it hasn’t had commits in the last 6 months. - How many open issues and pull requests are there? How old are they?
Too many open issues and PRs is a red flag. Issues and PRs should be actioned quickly. - How do the maintainers interact with community contributors?
Look for a library with friendly and helpful maintainers. Read through some issues and pull requests to see how the maintainers respond to community feedback or requests. Do they proactively fix problems and introduce features, or do they just tell people who open issues that they’ll accept a PR to fix it? Do they respond in a kind and supportive way or do they make people feel bad for asking questions or raising problems? However they handle existing issues is how they’ll handle your questions, problems, feature requests, and contributions.
Documentation
Documentation is one of the most critical things I look for in third-party dependencies. Many libraries, especially smaller ones, offer minimal documentation. Look for the following when you evaluate the docs:
- Do the docs cover non-happy-path scenarios?
If your use case is identical to the one the authors of the library intended, that will hold you for a while, but chances are that on a longer-term project, your requirements will eventually diverge at least a little from what they had in mind. - Is there a changelog or release notes?
Are you able to easily see what changes were introduced in what release, or do you have to read through the commits to find out what changed in a given version? - Are there detailed API docs?
A getting-started guide is great but when you’re dealing with real-world implementations it can only take you so far. Good docs talk give information on edge cases, limitations, problems, not just how the library works under ideal conditions or for specific use cases.
Security
No code is perfectly secure, so the thing to ask yourself here is whether the library is likely to be more or less secure than the code you write yourself. Well-known, actively maintained libraries with large communities of contributors are likely to be more secure than code you write yourself, and security holes are likely to be fixed as maintainers become aware of them. Libraries whose maintainers are experts in the problem space and technology involved are also more likely to be secure than those maintained by amateurs sharing their solution to a problem.
Testing
One of the core advantages to using a library instead of a home-rolled solution is that library code is often more testable. Relying on a library with high test coverage in a well-maintained suite of quality tests means you don’t have to include your own tests for that functionality. This advantage is lost, however, when you don’t have confidence that the library’s maintainers will establish and maintain that level of testing.
You don’t have to read every test in the suite to evaluate the type and quality of test coverage. Here are a few things to look for:
- What types of tests are included in the suite?
Are there unit tests, integration tests, acceptance tests, smoke tests? What framework(s) are used for each type of test? I like to see both some form of unit testing and some form of integration testing. - Does the library have test coverage metrics?
Test coverage metrics don’t tell the story, but they can act as a data point. Be careful of libraries that advertise 100% test coverage — it’s not generally feasible to achieve 100% quality test coverage, so projects that emphasise 100% test coverage often let the quality of those tests slide. I like to see at least 90% test coverage on the libraries I use, but 100% isn’t necessary. - Does the library have guidelines indicating that contributed code must be tested?
Working with community contributors is challenging — lots of people want to contribute quick solutions to open source projects without taking the time to properly test and document the code they contribute. Some maintainers will buckle to the pressure and let test coverage and docs slip. Good libraries will have guidelines (often in aCONTRIBUTING.md
document,README
, or pull request template) for including test coverage with all code contributions unless there’s a good reason not to. - What kind of feedback have maintainers left for contributors around test quality or coverage?
Looking at some larger pull requests against the library, have maintainers commented on the tests included in those PRs? What changes have they asked for? Comments focussed on test reliability, maintainability, approach, and coverage are all a good sign. Maintainers who never comment about testing are likely to not value it that highly.
Accessibility
Accessibility to assistive technologies such as screen readers is essential for modern user interfaces. If the libraries you’re considering involve UI components or features, investigate how accessible they are. Accessibility does not happen by accident. Designing accessible UI components takes time, energy, and expertise that many developers don’t have. If a library’s docs make no mention of its accessibility features, you should assume that it is not accessible to screen readers or other assistive technologies.
Social Responsibility and Inclusivity
Technical choices are political. Underrepresented people working on your team may be uncomfortable using libraries that use certain language. At a systemic level, the prevalence of tools using language like master
, slave
, whitelist
, and blacklist
can make the tech industry as a whole feel less welcoming to certain groups of people. Add this to language choices in docs (such as language assuming the reader is male) and the cumulative impact can be considerable.
Even if you don’t currently have anyone on your team who takes exception to any particular language, you want your team environment to be welcoming to whoever might join in the future. Additionally, how a team of maintainers handles inclusivity issues can tell you a lot about how that team will respond to the overall needs of their user base (that’s you!). Ask yourself:
- Has anyone raised any issues with language or other features of the library or its documentation?
Find out if there have been any complaints about the library or requests to the maintainers to change the use of language in their code. - How have the maintainers responded to requests for change?
Avoid libraries whose maintainers respond with hostility or defensiveness to feedback that their language needs to be updated. Besides being unfriendly to underrepresented people in tech, people who respond this way to requests for inclusiveness will often also respond poorly in other interpersonal situations where their existing point of view is challenged. - How diverse is the team of maintainers?
More diverse teams of maintainers are more likely to reflect the experiences of their users (i.e., the developers using their libraries) as well as end users. There’s also evidence that more diverse teams are more effective and make better decisions. - Does the community have a code of conduct? What is it?
Communities that lack a good code of conduct drive away talented contributors and tend to lead to a more homogeneous — and smaller — core group of contributors.
Conclusion
After hours invested in solving a problem, it can be tempting to install the first library you find that solves it. However, there are many more considerations to keep in mind than meet the eye when making this decision. I hope this post has shed some light on what considerations these are and equipped you to make the best decisions for you and your projects in the future.