.NET: specify constraints for NuGet package versions

A typical reference to a NuGet package in your project might look like

<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
Code language: HTML, XML (xml)

In the above example, we can see that we reference a package Newtonsoft.Json in a specific version, 11.0.1. We don’t specify any constraints for a person or a tool that would like to update the version in reference.

Now, let’s take a look how an IDE like Visual Studio will behave when we check if there are any updates to this package. As I write this, the NuGet server knows of the following versions of Newtonsoft.Json:

Exploring a list of all published package versions in NuGet.org service.

Default behavior: Visual Studio offers update to the latest stable package

Now, when a developer opens the “Manage NuGet Packages” window in Visual Studio and looks at outdated packages, she will be offered an upgrade to the most recent stable version:

Developer experience in Visual Studio 2022: the Manage NuGet packages window when constraints are not defined
Developer experience in Visual Studio 2022: the Manage NuGet packages window when we didn’t define version constraints

The policy to stay at the most recent stable version might be a good default. But there are situations when updating to the most recent version without thinking (whether performed manually, or with automated tooling like Dependabot) can result in problems.

Adding NuGet version constraints

There are situations, where we might not want to update to the most recent version. Some examples I encountered:

Example 1: in the .NET ecosystem, the major version of packages named Microsoft.Extensions.* should typically match the version of .NET. Otherwise, you risk runtime errors.

Example 2: In projects in the maintenance mode, you might only want to update with the security patches, but avoid including any new features or including breaking changes to minimize the risk and maintenance work. So, we might want to only bump up the {patch} part in a {major}.{minor}.{patch} version.

Now, we could try to just remember about such rules, but this is a poor idea. Another person in the team might not know about the convention. We might forget, and Visual Studio will suggest us wrongly. Automatic tools like the mentioned Dependabot will not care about such convention unless we are explicit in defining constraints.

Luckily, the <PackageReference> allows defining constrains. For example, the following definition will only allow bumping the package to the most recent 12.*.* version because the end of the range is at 13.0.0 (exclusive):

<PackageReference Include="Newtonsoft.Json" Version="[11.0.1, 13.0.0)" />Code language: HTML, XML (xml)

Visual Studio respects such constraints:

Developer's experience in Visual Studio when NuGet version constraints are defined for the package
Developer’s experience in Visual Studio after we defined NuGet version constraints for the package

Other IDEs and tools for bumping dependency versions should also respect that, as it is a part of the specification. See the linked document for more examples. And have fun bumping the packages! 🙂

Leave a Comment