Search code examples
.netnugetversioningmicroservices

What is the correct way of sharing common yet versioned infrustructural code between microservices?


We'd like to extract some infrastructure related code (mostly extension methods or small helper classes) into separate NuGet packages.

These packages in no way contain business logic pertaining to either of the services.

But as soon as as we end up having a hierarchy of dependencies between packages we are probably asking for troubles with versioning:

Service1 references package Av1.1 directly and Av2.0 indirectly via referenced package B.

ServiceN references package Av1.5 directly and Av1.3 indirectly via referenced package C.

NuGet does not support side-by-side versions and only one version is used per project. This problem of versioning is relevant for monolithic applications as well though with seemingly significantly fewer possible integration issues.

There are some ideas like not extracting common code and letting it be duplicated across services or just not changing the version of the package forcing every service to work with the same version.

Are there any good recommendations concerning code reuse between services which go well with versioning?


Solution

  • Using semantic versioning can help in this case by reducing or eliminating the amount of breaking changes your team experiences when moving between different versions of internal libraries. The core concept of semantic versioning is that major version increments signify the addition of backwards incompatible changes (such as removing or changing the signature of a method in the public API). By contrast, such backwards-incompatible changes must not occur between different minor or patch versions within the same major version. By following such conventions, your team should be able to avoid issues with your internal libraries not being compatible with each other (within the same major version). The Obsolete attribute is your friend here as well for marking code that will be removed in the next major version.

    Once your team uses semantic versioning for your libraries, a project can then (usually) safely reference different versions of the same assembly like in the second of your examples. These different versions can be unified through binding redirects to use the same version of that assembly (usually the higher version of the two). If you are using Visual Studio 2013 or later, these can be automatically generated and updated if this tag is present in the project files that consume your libraries. For your first example, if Av2.0 and Av1.1 have incompatible APIs, using Nuget will help by allowing you to update to Av2.0, test out that version, and rollback if something isn't working the way it should.

    From personal experience at work, I highly recommend using Nuget and semantic versioning instead of copy-pasting code or leaving all internal libraries at the same assembly version. Copying code around is usually considered bad practice (see this answer). Never changing the version number of assemblies makes it difficult to tell what code is actually in that assembly file without decompiling it.