Advice on Developing Shared Public Libraries in Visual Studio

githubnugetvisual studio

I’m looking for some advice on best practices for Visual Studio workflows.

I maintain several C# libraries which I use in various projects I develop. I also make them available publicly, as packages on NuGet and as source code on GitHub.
Because I am often tinkering and tweaking my common libraries as I use them, I typically add the ones I need in a particular solution as external/existing projects. To keep things organized I add them to a solution folder I set up called “external”:

how I currently reference/organize shared libraries

I can then use them in any other project within the solution by simply adding them as a project reference.

To ensure other developers will be able to build my projects from GitHub source code I also include references to their NuGet packages in the projects (i.e., I “double specify” the common libraries, once as a NuGet package and once as an “external” project). VS is smart enough to pick whichever version is more recent, so when I tweak something on my development machine the shared library gets rebuilt as part of the build process:

dual referencing shared libraries

This works well…for me.

But because this all gets published to GitHub it complicates the life of anyone who clones one of my solutions and tries to build it. Since they haven’t duplicated the folder structure of my development machines those “external” projects are undefined and cause builds to fail.

It’s easy enough to simply delete those external projects and the external solution folder. The required libraries will still be incorporated because I include references to their NuGet packages in the projects that use them (note that this requires I update any tweaked common library NuGet packages before I publish the app or library which uses the tweaked library).

I can solve the “missing external projects” problem other developers encounter when cloning my GitHub source code by simply deleting the external references when I push a main/master update to GitHub (and ensure the shared NuGet packages are also updated). But that requires me to remember to do that as part of the publication process…and go through the hassle of re-creating the "external" projects the next time I need to tweak something in the solution.

I could also simply tell other developers, via the README, that they should delete those external project references and the external solution folder. But that seems kludgy.

I’m curious if there’s a better workflow which continues to allow me to tweak/evolve my shared libraries but doesn’t require either me or other developers to jump through hoops to get things to build.

Best Answer

I think you've positioned yourself inbetween two workable scenarios and the hassle you're dealing with now is the result of not sticking to one scenario in particular.

You're using Nuget packages, which are great for versioning and publishing purposes, but this process takes a bit of time and complexity and is generally prohibitive of the kind of shotgun development where you are constantly making tweaks to your library while you are developing its consumer.

From a point of idealism, I would argue that you should not be tweaking your library at that stage. You've introduced a clear separation between the two and you should respect that separation. Your library is a different product from your consuming application, and you're mixing your concerns by trying to develop both at the same time.

Even if you were developing your consuming application and suddenly realized you needed an extra feature (or bugfix) on the library; I would consider this a change of mindset. You've halted the consuming application development, you are now doing some library development, which includes committing, any testing on your part, and publishing of the package; after which you can resume your development on the consuming application.

The main reason for doing so is that I think it's very important that while consumers' needs often lie at the basis of your library's requirements, the actual design and implementation of these requirements should be done in a mindset that does not have a specific consumer in mind. To this end, keeping the separation helps make that distinction clear in the developer's mind.

That being said, maybe this point is idealistic. Alternatively, have you considered a monorepo?

This would enable you to share your library across your services and synchronize commits to both the library and its consuming applications. You could still additionally publish your library as a nuget package for any consumers that are outside of your monorepo, but then they don't get access to the "quick tweak" versions of your library that your monorepo services have access to.

Monorepos also have their drawbacks; but your question does not reveal enough information for me to judge whether these are applicable to you or not. I suggest reading up on the pros and cons of monorepos before jumping in.

I want to point out here that I'm personally not the biggest fan of monorepos but your particular problem is one that they do help ease, assuming that the need to have fast turnaround on shared library code is an essential part of the development process for you.

Related Topic