Hi and welcome back to Weekly Dev Tips. I’m your host Steve Smith, aka Ardalis.
This is episode 36, in which we'll discuss a question I recently received about guard clauses and exceptions.
If you’re enjoying these tips, please leave a comment or rating in your podcast app, tell a friend about the podcast, or follow us on twitter and retweet our episode announcements so we can increase our audience. I really appreciate it.
Exceptions and Guard Clauses
This week's tip is on the topic guard clauses and exceptions. Specifically, whether and when it's appropriate to throw an exception in response to certain kinds of inputs.
Sponsor - devBetter Group Career Coaching for Developers
If you're not advancing as quickly in your career as you'd like, you may find value in joining a semi-formal career and technical coaching program like devBetter.com. I launched devBetter a few months ago and so far we have a small group of motivated developers meeting every week or two. I answer questions, review code, suggest areas in which to improve, and occasionally assign homework. Interested? Learn more at devBetter.com.
Show Notes / Transcript
As a bit of background, I described Guard Clauses in Episode 4. If you're listening to these shows in most-recent-first order, I suggest you configure your podcast app to let you listen in order and then start back with episode 1. You'll thank me later. In any case, a guard clause is a check you make at the start of a function or method that throws an exception if some input is not valid. For instance, you could have a function that is supposed to send an email to an email address that's provided as a string argument. If the string is null, it might throw an ArgumentNullException or something similar before attempting to create and send the email. If you like the guard clause pattern, I have a nuget package you can use to easily add and extend them in a consistent fashion in your applications - look for the link in the show notes.
Listener Jeremy C. writes:
i discovered your podcast recently, and am going through it. I love the Guard class shortcut for happy path.
What i noticed, is that this behavior relies exclusively on throwing exceptions rather than what I learned (20 years ago) in school that exceptions are for the unexpected, and if you can reliably prevent throwing an exception, like checking for a null and handling it, you should to avoid the execution cost of exception handling.
Is my information just out-dated? :) Perhaps I'm too old of a coder and too many old habits are stuck.
It's true that exceptions should not be used for control flow in your applications. That means if it's a normal condition for a function to get a null as an argument, perhaps because that situation means "create a new thing" instead of "do something with this thing", then you wouldn't want to write code that depended on an exception being thrown for its behavior. For example, you wouldn't want to have a try block that tried to work with the object, and then a catch block that caught the NullReferenceException when it was null and created a new instance there.
There's plenty of reference material you can find about why this is considered a bad practice. In my opinion there are two main reasons. One is performance - exceptions are far more expensive than if statements so you shouldn't use them where an if statement is more appopriate. This is the main one, and because of it, writing code that leverages try-catch statements for something more than error handling is unexpected. Developers will be surprised to see this approach, which violates the Principle of Least Astonishment. You want readers of your code to be able to immediately understand what it's doing, and surprising them by doing things in odd ways is contrary to that goal.
Coming back to guard clauses, the idea is that you're setting up an expectation that under all normal conditions in your application you expect that these arguments will follow certain constraints. If they don't, the program's simply not going to work the way it should. In this case, an exception is the appropriate response and is more elegant than any other solution like returning a boolean value or magic number or null and the caller having to know to check for that result.
Thanks Jeremy for the great question! After I answered, he summed it up like this:
So the Guard Clause is for the situation of, "I told you the expected inputs, and while I'll protect myself from bad data, I'm not going to make a huge mess of if statements to protect myself, I'll pass the mess back to the code giving me crap."