Alright... I'm done chopping my code into microscopic namespaces. There's just no point. All it does is force a bunch of using
statements all over the code (both in the project itself and in any project consuming it as a library).
The most common place I see this unhealthy obsession is in your typical ASP.NET app. Devs feel this compulsion to make a namespace for every kind of component.
namespace MyApp.Web.Controllers;
namespace MyApp.Web.Models;
namespace MyApp.Web.Services;
namespace MyApp.Web.Utils;
And this is a kind representation. I've even seen projects isolate Models.Request
from Models.Response
. Personally, I do not think this is necessary or helpful. In my more recent endeavors, I've settled on just using MyApp.Web
and stopping there. It gets rid of a ton of useless using
statements, and all the identifiers I need are now properly visible. I still find it acceptable to put the other whole layers into separate namespaces (MyApp.Domain
, MyApp.Repository
, etc.).
I fell prey to this in Jawbone. I think there is just something alluring about "being as cool as the .NET base classes". Hey, .NET has a namespace for its collections, so I should have a namespace for my collections! There's just one problem, though: .NET is huge, and my library is not. When you reach a certain size, it makes sense to gently categorize various components. Also, if code exists in distinct projects, having a different namespace makes sense.
So, I've taken the first steps to wrangling this mess. I am eliminating the Piranha.Jawbone.Tools
and Piranha.Jawbone.Collections
namespaces. In my mind, there are only two scenarios that justify a new namespace:
- Strongly distinct domains: I am keeping namespaces such as
Piranha.Jawbone.Sqlite
andPiranha.Jawbone.Sdl
. Those are highly cohesive domains that could arguably even be spun off into their own libraries if the need arose. They revolve around major native components and have clear delineation. - Isolation of extensions on common datatypes: if I want to introduce extensions for common types like
string
orDateTime
or whatever, I don't want those extensions flooding devs' autocomplete. It's polite to let them opt in byusing Piranha.Jawbone.Extensions
. They should not pay that price just forusing Piranah.Jawbone
. Meanwhile, for new datatypes introduced by Jawbone itself, the extensions just sit in thePiranha.Jawbone
namespace.
Already, I am enjoying the drastic reduction in using
statements.
Top comments (5)
Agree to disagree. Strongly.
Well don't leave me hanging. Go ahead. Let it out.
I don't disagree with everything you say, of course. There is no question that namespaces, like anything, can be overused and burden a codebase more than benefiting it. I like that you frame the potential for disconnected libraries as a good justification for grouping types together in a namespace. I feel the same. I appreciate that your article focuses on code hygiene for the sake of the next developer. I think many architectural decisions overlook this important perspective, and it's a great point to make.
However, there's no denying that your overall motivation, which bookends your article in the first and last paragraphs, is to avoid the nuisance, clutter, and noise of
using
directives. (It's worth noting that in C#, formally, these areusing
directives _ and notusing
_statements. Though I think we've all said it both ways at times.) I mean, Kelly, you double down to the point where you even diagnose the abundance of namespaces and the collection ofusing
directives as an unhealthy obsession. Unhealthy? How many is healthy? Who arbitrates?Look, I acknowledge your point that breaking every type into a namespace would be ridiculous. This is especially true, as you noted, in a smaller codebase. That said, consider these three notes:
using
directives can be automatically removed through code cleanup, which is natively integrated into the IDE.using
directives can be collapsed into a single line, taking up just one line in the editor regardless of how many there are.global using
directives, you can automatically include all the directives you need in every file in a single place, hidden from view.The main reason I prefer to err on the side of too many namespaces over too few is because I recognize their benefit in terms of discovery. You gave the example of
Models.Response
andModels.Request
, and I would argue that this is very reasonable. Very. When you are responding, Intellisense will present to you a discrete list of models that are appropriate for the operation. Without this, you are just fooling yourself becauseResponseModel
orRequestModel
, or something like that, would end up being part of the name in some other way - otherwise, how would you ever know which is appropriate? More importantly and to your other point, how will other developers know?It's okay we don't agree on this. We probably agree on a lot of other things. Your article was thoughtful, articulate, and worth my time. Don't be discouraged by this comment from me. I mean, seriously, who am I? Every developer doesn't need to be the same. Every dev team does not need to be the same. I will consider the points you made more. This would not be the first time my opinion on something is wrong. Maybe I am. Thanks.
Best of luck,
Jerry Nixon
Hey, I appreciate the thoughtful response. I'm not the authority over all C# written everywhere! I'm just sharing my feelings. :)
Perhaps I overstated my case, but just to add some perspective here: I have worked in code bases where, upon opening a source file, you are greeted by multiple pages of using directives. (Directives! Not statements! Thanks for the correction!) Perhaps that is indicative of other problems, but I do think that a big part of the problem is the fact that the classes are sitting in way too many namespaces in a given library.
Who arbitrates? Me! It's my blog! My opinion is king here! But in all seriousness, I call it unhealthy because it's a simple case where I feel the actions cannot be reasonably justified. I feel like people do it only because they have seen other people do it.
This is a separate discussion. Despite what I've written here, I don't like global using directives and feel that it largely defeats both our points. I actually like seeing what other namespaces a given source file explicitly accesses; I just think that one namespace (give or take) per library is enough.
This is a strong argument. I do think there is validity to this aspect. This isn't really how I personally navigate a new library. (I just have the docs open.) But it is something I'll have to keep in mind.
So, a few thoughts here. First off, are you reusing models? How extensively? A lot of APIs I've worked in tend to have one specific request model and one specific response model for one particular action/endpoint. Occasionally, the response model is reused across a couple HTTP verbs, but even in such a case, the actions are typically developed together. So, I'm rarely (never?) in a situation where I would want to discover the right models using Intellisense.
From there, are you making models with identical names?
Request.GetResource
andResponse.GetResource
? This leads to ambiguity and basically prevents you fromusing
the namespaces here. So, yes, I would actually rather just have aGetResourceRequest
and aGetResourceResponse
.I was going to say something similar. I suspect we're more alike than different. I've simply had experiences that led to me producing today's rant. I'm grateful you're not the average Twitter user here to insist that I'm a bumbling buffoon who will never amount to anything in the world of .NET.
I wish you luck in your endeavors. Thanks for taking the time to read my words.
All great points.
And as far as one coding file with multiple pages of
using
directives? Shameful. I would love to have an engineer try to defend that with a straight face. Maybe "unhealthy" is exactly the right word choice - in this case - and, besides, it is your blog - not mine.Thanks again for the exchange of ideas. 2 peas in a pod, I'm sure.