A recent discussion on Twitter got me thinking again about this naming convention in .NET that I'm not a fan of. Since my most recent contract just ended (hint hint) and my DevOpsDays PDX talk has been presented (also hint hint) I finally have some time to blog again!
This all started out a couple days ago with an innocent question:
Here's where I was introduced to it yesterday morning:
Should you suffix Task-based methods with Async? Yes. https://t.co/PFftsc1r0v— Immo Landwerth (@terrajobst) August 25, 2017
Then this comes in last night:
No.— Damian Hickey (@randompunter) August 25, 2017
Suffix non-task based methods with Sync.
And if your API is mixed sync and async, it is probably broken. https://t.co/tz0pDS5AID
Essentially, the discussion comes down to this: which of the following is correct?
My preference is definitely the latter, and the reasoning behind it is this:
The name of the method should tell you what the method does. Not how it works; what it does.
The rest of the signature tells you how it works: the accessibility level, the return type, the parameters, ..., that it's asynchronous.
It's the same reason we don't use Hungarian notation anymore (its original usefulness aside, given later rampant abuse). Having a great type system, and tools that allow us to easily determine and work with those types, has removed the need to shoehorn that information redundantly into the name.
The same goes for async: we've got a keyword for it, take it out of the name. If your tools don't allow you to easily see that it's async, get (or make) better tools.
Great, so we've decided this is best:
But what if you've got a class that does both synchronous and asynchronous versions of the same methods?
Stop right there, go back to Damian's tweet, and read the last sentence again.
No, actually do it.
If your API is mixed sync and async, it is probably broken.
Update @ 8:15pm: Clarification — my own stance is a little more conservative, replacing "broken" with "not ideally designed."
The "correct" architecture is the following:
"Hold up!" you say. "Isn't that the exact same as what you were talking about before? Where'd the synchronous one go?"
If the job of getting a number is inherently asynchronous, that should be the default implementation. Making a synchronous version of the same thing is either a workaround for consumers without an async context, or else it should be deprecated because that was the old way of doing it. If the synchronous version doesn't need to exist at all, remove it. If you absolutely need both a synchronous and asynchronous version, they belong in separate classes.
OK, ready to yell at me about this? Use the "Comment / Share" link below!