dotnet icon indicating copy to clipboard operation
dotnet copied to clipboard

[Feature] Introduce a source-only Microsoft.Toolkit.Diagnostics NuGet package

Open Sergio0694 opened this issue 4 years ago • 4 comments

Describe the problem this feature would solve

The Microsoft.Toolkit.Diagnostics namespace contains the Guard and ThrowHelper APIs, which are very useful for developers on any platform and working on all kinds of applications, to do input validation and control flow management. Right now though they live in the base Microsoft.Toolkit package, which has a few downsides to it.

  • Contrary to other packages which have APIs that all belong to the same set of features or that pertain to a specific topic (eg. "Animations" or "Controls" or "Markdown"), the base Microsoft.Toolkit is a bit of a container for a number of different APIs that are unrelated from one another. Specifically in the context of developers purely interested in using the .Diagnostics APIs, having to also pull in unrelated code is not ideal. For example, the grouped collection types - which are more of a UI application type of APIs that might be completely out of place in other kinds of backend scenarios.
  • It includes an external dependency to the Microsoft.Toolkit package. I've spoken with a number of developers, and this is very often brought up as a main annoyance point, if not just a reason not to pick up the package at all. This is especially true for library authors, since those dependencies would transitively be inherited by consumers of those libraries as well, and be listed on NuGet, and just result in extra assemblies in the output folder of consumers. This can of course be absolutely fine in case of consumers being application developers, or just of consumers using bigger packages that are to be expected to involve a separate assembly (eg. the MVVM Toolkit, or any of the UWP packages), but it's often not received well or considered worth it in case of a very narrow scoped set of self-contained APIs such as the .Diagnostics ones.
  • Even if the package only contained the Guard and ThrowHelper APIs, those would still then be visible to consumers of those libraries, as indirect dependencies, even though the library authose only actually wanted to use them as internal helpers. Furtermore, because the Microsoft.Toolkit package contains some other unrelated APIs as well, consumers of those libraries would end up seeing those as well, even if they have nothing to do with the library they're installing.

Describe the solution

I feel that the Microsoft.Toolkit.Diagnostics namespace fits all the boxes to deserve to be moved to a standalone, self-contained, source-only NuGet package. This would greatly help with adoption especially for library consumers:

  • [X] No additional project dependencies
  • [X] No more transitive dependencies for consumers of those projects
  • [X] No additional APIs exposed from consumers of those projects

For more info on source-only packages, here's a useful guide.

Further considerations

The Guard APIs shipped with the Microsoft.Toolkit 6.1 release, and we can't just remove them already. Plus I guess there could be many developers actually expecting of finding them there by default when referencing Microsoft.Toolkit or any of the other dependent packages. I'm thinking the Microsoft.Toolkit.Diagnostics package could just be an additional package that developers could choose to reference instead, without necessarily replacing the APIs in the base package. Again this would be very valuable especially for backend/library developers - they'd have the option to just use the source-only package purely as a development dependency (just like eg. StyleCop or Nerdbank.Versioning), without having their product actually depend on them on NuGet.

Case study: ImageSharp

ImageSharp is a very well known and widely used image processing library for .NET, arguably the best one there is. It's a .NET Foundation sponsored project, with 4.4K stars on GitHub and over 8 MILLION downloads on NuGet.

I proposed to use the Guard and ThrowHelper APIs to replace the internal helpers we're using there, but understandably Anton and James said it wasn't worth it for them to take an external dependency just for that. I spoke with @jimbobsquarepants yesterday about this new proposal for a source-only NuGet package, and he said he'd be on board to replace their helpers with our Guard and ThrowHelper APIs if we released them as a source-only package. I think having them adopted by such a big library would be a great way to showcase how useful these APIs are, and also to showcase how the Toolkit in general can be useful for library developers and cross platform developers that are just working on .NET Core/Standard, and not necessarily UWP. 🚀

Sergio0694 avatar Nov 29 '20 12:11 Sergio0694

Hello, 'Sergio0694! Thanks for submitting a new feature request. I've automatically added a vote 👍 reaction to help get things started. Other community members can vote to help us prioritize this feature in the future!

ghost avatar Nov 29 '20 12:11 ghost

@Sergio0694 would it make sense to keep the binaries in the Microsoft.Toolkit or a Microsoft.Toolkit.Diagnostics package, but then have a separate Microsoft.Toolkit.Diagnostics.Sources package like the examples in the article you shared? That'd help if our other packages start using them as they wouldn't be duplicating the source for each package using it, eh?

michael-hawker avatar Nov 30 '20 18:11 michael-hawker

@michael-hawker I'd say that regardless of the proposal for a source-only package, the idea of moving the Guard and ThrowHelper APIs to a separate, standalone Microsoft.Toolkit.Diagnostics package sounds great to me anyway 😄 That'd still mean that consumers would have more fine grained control over what APIs they're using in their apps, sure!

As for the bit about duplicating the sources in the other Toolkit projects, not sure that that'd be an issue, since we can just use [assembly: InternalsVisibleTo("...")] so that all the other projects would be able to use a single "copy" of all those APIs that they have access to through the base/diagnostics package. Of course that also depends on how we actually want to set that up though, as in, we might also just choose to depend on the .Diagnostics package too and consumers of the other packages would automatically inherit those APIs as well (like they do today). That depends on how we want to structure this, since those APIs would still be included in the actual binaries anyway (as other packages do use them).

As for the Microsoft.Toolkit.Diagnostics.Sources package, that name sounds perfect and it would convey the difference pretty clearly (so that other people would immediately know they won't actually take dependencies at runtime), so that sounds great! 👍

Sergio0694 avatar Nov 30 '20 18:11 Sergio0694

Even if the package only contained the Guard and ThrowHelper APIs, those would still then be visible to consumers of those libraries, as indirect dependencies...

I've run into an issue related to this in the wild. One of my dependencies still uses the Microsoft.Toolkit.* packages, while my project uses the newer CommunityToolkit.* packages. The result is that my project has the same functionality under two different namespaces.

yoshiask avatar Feb 24 '22 02:02 yoshiask