When to throw exceptions

I’ve written several posts about exceptions. For instance when you should catch them or how you should design them. In this post I’ll discuss when you should throw exceptions.

Exceptions are thrown when something exceptional have happended. But what does that mean? In fact it’s really simple. Let’s look at an example interface:

interface IUserRepository
{
	void Save(User user);
}

You should throw an exception every time that method fails to save the user into the data source. The failure reason do really not matter. All of these reasons are valid:

  • The user object do not contain all mandatory information
  • The user object is null
  • The data source is off line
  • The data source rejected the save.

Why? Because the method is named Save and returns void. That is, the contract of the method indicates that a save should in most cases be successful. Hence something exceptional have happened if the user is not saved into the data source.

But couldn’t you have written something like this instead:

interface IUserRepository
{
	bool Save(User user);
}

Yes, sure. That method would return false instead of throwing exceptions. But the method contract indicates that the method can fail as many times that it can succeed. You really do not mean that, do you?

but.. but… The Remove method usually returns a bool.

interface IUserRepository
{
	bool Remove(User user);
}

Yes. It do. The reason to that is that it says that it’s OK to call Remove even if the user do not exist. imho it’s a vague contract. It’s not clear if the method means that the removal is expected to fail or if the method allows you to remove non-existing items. I would have used void as return value instead.

Myth: Exceptions are expensive

First of all: Exceptions that are not thrown are not expensive.

And if you start to think about it, exceptions are actually a lot cheaper than using return values. Exceptions forces you to take action, no action means that your application crashes. The exceptions also include the exact location of the error. Return values only work if you check and handle all of them. I would like to see any reasonable sized application which uses return values where all of them are handled.

The point is that it’s a lot more tedious task to find and fix bugs in applications that uses return values instead of exceptions since you have to use details logs or reproduce errors to be able to locate and fix them. The amount of time spend to locate and correct errors probably costs a lot more money than buying new hardware to make up for the exception performance penalty.

Summary

Throw exceptions if the method contract is violated.


  • Herbie

    Interesting post. However, I would point out that there are times when you want the _caller_ to make the decision about whether they care or not about the result.

    For example: a method that updates a log file. In some scenarios it is important that data is logged (e.g. details of a credit-card payment), while in others it is less important (e.g which user changed the schedule for clearing old temporary files).
    If we stick rigidly to the ‘exception when the contract is broken’ rule then we are forced to handle issues that we don’t necessarily care about. If we use a return value, then we can decide whether we care about the result in a specific case. This isn’t an issue about performance, this is an issue about who is in control of the error handling — the caller of the method, or the method itself.

    I like your explanation of using exceptions to show a failed contract, and I would encourage it’s take-up for core functionality, I just want to point out that rules such as this should be applied with forethought for the context of the process.

  • Timothy Fries

    It can lead to confusion in your code base if you mix approaches, though, so deciding which behavior you’ll adopt on a case-by-case basis isn’t advisable. ‘Exceptions when the contract is broken’ is the more correct approach, so as a general rule, that should be the one you follow.

    If you have situations like your logging example where failures aren’t critical, you can adopt a pattern that makes the behavior clear by prefixing the imperative method name with ‘Try’, as Int32.TryParse does. That way you’re even still following the ‘exceptions when the contract is broken’ rule; since in this case the contract is only to *try* to write to your log file, not to actually make sure it gets done.