Choosing Package Dependencies

Applications today are built upon well-tested, reusable packages. These packages solve the most common aspects of software development, such as logging, authentication, handling HTTP requests, serialization etc.

It is a regular task for a development team to decide which dependency to take on. New features will be built-up these dependencies. A new dependency is an additional responsibility for team: updating and replacing the package without breaking the features built on it. Updating non-breaking or patch releases is a straightforward task, however replacing a library or updating it with breaking changes can be extremely costly. I have come across teams stuck with no longer maintained libraries for years, before the team managed to completely get rid of it. Any security vulnerabilities during this period pose a risk. Therefore, the decision for picking the right library has a huge weight.

I very often observe teams deciding application dependencies based on the packages' popularity. I think this is a fraud way of picking libraries. In this post I summarize the questions that I prefer to ask when I have to introduce a new dependency in an application. I expand on my questions in the space of .NET, C# and NuGet packages. I know some of the languages are vastly different to the NuGet package ecosystem, for example npm is has more and tiny packages. Developers of such ecosystems might take my concerns with a grain of salt and apply that best works for them.

NuGet Packages

NuGet is the package manager for .NET. It consists of multiple parts, a package repository and CLI tool to install, update and remove packages. Transitive dependencies are present but not overwhelming. Open-source and non-open-source libraries can be both packaged into NuGet packages and published to nuget.org. Alternatively, an organization might publish non-public packages to an internal package repository using the NuGet protocol.

Licensing

The licensing information of a package typically has larger implications, for example certain licenses might not be acceptable for an organization. The license determines the usage rights and restrictions. Not all open-source licenses allow developers to freely use, modify, and distribute the software. By understanding the specific terms and conditions of the license, developers can ensure that they comply with legal requirements and avoid any potential copyright infringement.

Do I really need a package?

As described above, introducing a new dependency increases the maintenance burden of the dependency. Not using a library means that the development team needs to spend time to implement a similar solution by hand. Implementing by hand means more code to test, maintain etc. It is best to spend time on features that bring competitive advantage to the organization. When a package solves a common software development problem, it is likely the right choice. When a package solves a highly specific problem that still requires a large amount of customization, it might be better to have a custom implementation. For example, I have packages developed in my free time, that are copy-pasted into closed source applications, because that serves better that given organizational needs.

What are my choices?

Typically, there are multiple packages that can help us serve a problem. For example, when developing UI applications with .NET we could use Avalonia, Uno, MAUI, WinForms, WinUI, WPF, etc. frameworks. All of them help with creating UI apps, however all of them has different approaches hence pros and cons. I usually prefer to outline in an Architecture Decision Record (ADR) all reasonable options.

Open-Source Repository

Next, I evaluate following questions when the repository open-source:

  • Is the code archived? If so, it is likely a poor choice for future support and bug fixes.

  • Is the code actively maintained? Are there new patterns and best practices incorporated? What made sense in C# 1, maybe has better alternatives with the latest C#. For example, async code is handled vastly differently in different .NET versions that is reflected usually on the API surface too.

  • Are new features added to the codebase? For example, does a logging framework log into files and console only, or it picks new practices such as OpenTelemetry? Please note, that at the time of writing OpenTelemetry is relatively a new concept.

  • Does it target modern .NET releases? For cleaner syntax with modern C# and faster code execution targeting multiple .NET versions is essential.

  • Are there open issues? Is there an active community, does the maintainer accept issues? If not, how can I submit a security vulnerability or report a bug?

  • Does the owner of the repository respond in a timely manner to the issues? Some owners do not respond to issues for weeks. How can I handle issues that I face in my application daily?

  • Are there multiple contributors? Is there an active community, or a single person / small group wrote most of the code? Is the code readable enough so that I can contribute to it? For example, if the person gets into jail, who is going to maintain the package?

  • Is there a published roadmap? Does the project have a set vision? How does my vision of using this library match with the published roadmap?

  • Does the repository's published license match with the license of the package?

  • Does the codebase have multiple branches? Are there radically new versions of the project released? For example, is there a branch for v2 or v3?

  • Are there other open-source packages depending on it? What are those projects?

  • What are the transitive dependencies? Is it only what is necessary? Are there too many transitive dependencies? Could any of those cause a conflict for my project?

  • Are there open PRs, constructive code reviews, etc.? Is there a healthy community, when I have to extend the dependency with a new feature?

  • Does the project have a CI/CD pipeline or is built by hand? How and when does a new release become available?

  • Does the codebase have good tests (to avoid future regressions)?

  • Does the project have preview builds to test out new features or early bug fixes?

  • Is there a single person as the maintainer or an organization?

Organizational Needs

One of the most important questions for organizations is if there is a support plan for the dependency. I find it rare to spend team efforts on fixing bugs in an open-source package, given the team needs to implement the features that bring the competitive advantage. Even worse, an organization might not allow to contribute to open-source projects in working hours. It is rarely allowed to implement features for an open-source library that is owned by another company. On the other hand, the owner of a package must also have an incentive to fix a bug or implement a new features. This incentive is usually achieved with a paid support plan and a contract. Does the line of management prepared to pay for such contracts if ever needed?

Conclusion

In this post I summarized most of the key questions I have in mind when selecting a new dependency of package. The questions are not in a priority order. Organizations have different needs weigh some of these questions more than others.