Nuget is bad - really bad
Most of us don’t like to badmouth open-source software. Most of the contributers are not paid, they do it for the community and we don’t pay for the software etc. but I don’t think this is actually always the correct view.
With Nuget, it is built-in to dotnet. I really don’t know of another package manager that is even remotely suitable as a replacement. MS have adopoted it into their fold and the rest of us have to live with it. If you look on github, you will see thousands of issues raised about it. Some, to be fair, are minor or edge cases or might not even be bugs but there are a stack of things that make it almost unusable even in relatively simple setups.
Here is a list of all the things I have found that majorly impact it as an effective manager in a modern environment
A whole load of stuff is related to slowness. I was just trying to resolve conflicts in a solution with 7 projects and after an hour, I gave up. Slowness includes:
- The package manager interface in VS is slow as anything. Click on the browse tab and it is doing something slow. Click between tabs, it takes ages.
- It reloads lists incessently. If it is slow loading a list of e.g. upgrades, you click away and back and it loads it again. Why? Some basic local caching would be easy enough and it already has a refresh button
- Searching for a package takes ages. Why? Can the nuget website not cache the entire list of names in a small amount of RAM and return them in milliseconds?
- When you click on a package to e.g. upgrade it, it then loads the right-hand side panel. If the website is slow, this doesn’t come up, sometimes for 10seconds+. Why isn’t this list cached on the local machine with the packages? The assumption should be that you never change a package after publishing so you should never need to keep refreshing the list
- Searching for the correct package based on the framework of the project then takes even more time, even though you have already queried the data source several times. Why doesn’t it already know what it needs to install for a given framework and even if it did need to query this, again, why is it not heavily ram cached.
- When you add/update a package, the lists are always refreshed again. Why can’t it just refresh the item(s) that changed? On some tabs, especially if a feed is running slowly, you can be waiting for 5+ seconds for each refresh. Multiply that by the 10 packages you are updating just in one project and it quickly becomes bothersome.
- It is pretty much impossible to consolidate nuget packages from a single place. Almost invariably you have to jump pseudo-randomnly between the nuget solution editor and the nuget manager for individual projects. This is mostly for the other mentioned reasons like you cannot say, “update this to a version and do all the dependencies at the same time”
Errors and messaging
- This has only got marginally better with newer visual studio versions and it isn’t helped by the fact that they still don’t seem to encourage/insist on using colours to make the verbose messages more readable. When it fails to install a package due to a dependency, it usually takes clicking on the errors or output tab, reading the verbose messages, and then working out in your head how to resolve them. Do you know what a sensible person would do? Get it to tell you “we can’t install this because of this, do you want to change the dependencies to make this happen?”
Versioning and dependencies
- This is done differently by different people. Of course, it is open source, you can’t exactly police it but you could for example require that anyone who publishes to nuget.org support a certain way of doing things. There are issues around some packages are multi-targetted so can always be updated. Others have versions pinned to versions of dotnet so they cannot always be updated even though you can sometimes perform the upgrade and it just falls over later. This is especially a problem because of MSs bullish approach to changing not just version numbers but fixes only going into the latest versions. Things that were short-lived like netstandard and various other attempts to be completely portable are all skeletons waiting to bite. They might work with certain libraries, they might not.
- Although you can multi-select packages to upgrade to the latest version, you cannot multi-select to upgrade or downgrade to a specific version. If you updated everything to dotnet 6 and then realise this doesn’t work, you need to downgrade everything to dotnet 5 but if you do them in the wrong order, it won’t let you because of dependencies. Again, it would be nice if it could work out, “if you do this, I will need to downgrade these others too, is that OK?”
- It doesn’t seem to prevent incompatability between versions, even if library A v1 needs library B v1, you can often install v2 of B, it might install, it might even run and it might work but equally, you can find at runtime that something crashes because although a method matches a signature, a change in library B v2 now makes it fall over. You should never be allowed to successfully build these sorts of broken dependencies unless ou enforce the need for backwards compatability and provide clear guidelines on what might break something innocently like adding additional overloads of a method or throwing an exception where you used to ignore something.
- If netstandard allows multi-targeting then when this is added to an application, nuget should then treat the various targets more strictly. If your netstandard doesn’t target the application version - error (or overridable error). If it does support the framework, nuget should check the specific dependencies and again report real errors. You are targetting two different versions of the same package, this will not work.
- The MS mindset of only fixing things in new versions is not compatible with nuget and should not be done that way. It’s easy for them to say, “just update to .net 6” but there are a tonne of footguns that make this impossible in many scenarios. The implication that at the slighest whim, I could simply update an old web forms app or MVC5 to dotnet core or even dotnet 5 to dotnet 6 is ridiculous. If you are going to fix something in net6 then build your nugets in a way that the same file fixes all the older versions too.
- The fact that you often reference a third-party library that references a different third-party library makes the versioning issues really important. Library C makes a breaking change and doesn’t create a new library/nuget package. I use library C directly but it is also used via library B. If I update it, it breaks because library B won’t find the code it expects at runtime. If library B updates it, it forces a change to all upstream projects which might not be ready for the same reason. Breaking changes should either become separate packages or there should be another primitive so that you can clearly see that you are using the latest libraries you can within a stream but not necessarily the latest version of the library.
- MS seems stuck between whether versions of nugets map to dotnet versions (in many framework type libraries they do) and whether each package should simply have its own versioning. In some cases, updated libraries don’t have any changes, they were just updated so that v6 of A could point to v6 of B and v6 of C, which just increases the chance of getting stuck in dependency hell. If System.Memory.Caching has never been changed, leave it at v1 otherwise you can create an error that doesn’t actually exist because nuget thinks that v5 != v6.
- Version pinning doesn’t work. How many times have you seen a package downgrade error between e.g. v1.1 and v1.2? They are minor versions and the dependency is supposed to be pinned to the major version. You end up having to keep working backwards to find what packages you can install first with no dependencies.
- It would be really good to have some global functionality solution-wide that actually worked effectvely. “Manage nuget packages for solution” is far too slow to consolidate so instead I should be able to say, “update all packages to latest” or “fix all dependency issues” and that should be pretty easy, even if the result does not build or work, at least you can see if you want to update library A from v1 to v2, how much other risk does it introduce with the dependency changes.
- Package reference format has been really useful for seeing dependencies and dependencies of dependencies but they never added the feature for ASP.net projects on netfx. Again, easy to argue that MS would rather work on newer stuff but wouldn’t we all? Many of us have to live with old apps that would be months or years of work to migrate and in the meantime, MS are saying that we can live with the misery of packages being added and never removed.
- The old nuget format (not sure if they ever added this to the new ones) could do things like update web.configs with redirects and add other files. Often the reverse was not true. Don’t let people do that. If you need a config file for the feature, add one and copy the contents from the web site.
- The fact that nuget processes web config differently than visual studio means that you often see hundreds of changes in a file where one might add spaces in tags and the other doesn’t.
- It is possible and not uncommon for an error during an attempted installation/downgrade, for the packages.config to be updated but the csproj still pointing to the older location in packages. Works fine locally because you have both locations in packages. Fails on the build server and it’s really confusing because of the verbose error messages.