Use exceptions for exceptional things, the things you can't reasonably expect to encounter too often, things which indicate that something goes wrong. For example, if the network is down, it is an exceptional thing for a web server. If the database is unavailable, it means that something is wrong. If the configuration file is missing, it probably means that the user messed up with it.
Don't use exceptions to handle incorrect code. In order to check the correctness of the code, you should use either the assertions, or, in .NET Framework 4 and later, Code contracts (which replace assertions and have additional, particularly valuable features).
Don't use exceptions in non-exceptional cases. The fact that the user, when asked to enter a number, entered "dog" is not so exceptional to deserve an exception.
Be careful when choosing the types of exceptions. Create your own types when needed. Carefully chose the inheritance, keeping in mind that catching parents will catch the children as well. Never throw Exception
.
Don't use return codes for errors. Error codes are easily masked, ignored, forgotten. If there is a error, either handle it, or propagate it to the upper stack.
In cases where a method is expected to return a error and the error is not exceptional, use enums, never error numbers. Example:
// Note that the operation fails pretty often, since it deals with the servers which are
// frequently unavailable, and the ones which send garbage instead of the actual data.
private LoadOperationResult LoadProductsFromWeb()
{
...
}
The meaning of LoadOperationResult.ServerUnavailable
, LoadOperationResult.ParsingError
, etc. is much more explicit than, say, remembering that code 12 means that the server is down, and code 13 — that the data cannot be parsed.
Use error codes when they refer to the common ones, known by every developer who works in the specific domain. For example, don't reinvent an enum value for HTTP 404 Not Found or HTTP 500 Internal Server Error.
Beware of booleans. Sooner or later, you will want to know not only whether a specific method succeeded or failed, but why. Exceptions and enums are much more powerful for that.
Don't catch every exception (unless you're at the very top of the stack). If you catch an exception, you should be ready to handle it. Catching everything is showing that you don't care if your code runs correctly. This may solve the "I don't want to search right now how to fix this", but will hurt you sooner or later.
In C#, never rethrow exceptions like this:
catch (SomeException ex)
{
...
throw ex;
}
because you're breaking the stack. Do this instead:
catch (SomeException)
{
...
throw;
}
Make an effort when writing exception messages. How many times I've seen something like throw Exception("wrong data")
or throw Exception("shouldn't call this method in this context")
. Other developers, including yourself six months later, would have no idea what data is wrong and why or why shouldn't we call some method in a context, nor which context precisely.
Don't show exception messages to the user. They are not expected for ordinary people, and often are even unreadable for developers themselves.
Don't localize exception messages. Searching the documentation for a localized message is exhausting and pointless: every message should be in English and English only.
Don't focus exclusively on exceptions and errors: logs are also extremely important.
In .NET, don't forget to include exceptions in XML documentation of the method:
/// <exception cref="MyException">Description of the exception</exception>
Including exceptions in XML documentation makes things much easier for the person who is using the library. There is nothing more annoying than trying to guess which exception could be possibly thrown by a method and why.
In this sense¹, Java exception handling provides a stricter, better approach. It forces you to either deal with exceptions potentially thrown by the called methods, or declare in your own method that it can throw the exceptions you don't handle, making things particularly transparent.