Errors should always be caught and logged – there are plenty of great services that help you fall into the “pit of success”. They have great dashboards, integrate with your preferred communication tools, give you great telemetry, and help you drill down to the root cause.
As developers you should be alerted when something is going wrong and be able to see details to help you track down and fix bugs. Every developers goal should be to know about bugs before the clients do. With exception libraries, you should already be on it.
For developers, it is crucial to log errors, watch them daily and review the health of the errors in the Sprint Review.
And think about the end user for a second…. we’re all aware how painful it can be when you see a nasty error message. In the best case it makes you feel uneasy, and at worst it leaves you blocked. The user experience should *not* be an jarring jumble of text that looks like it is designed for a computer 🤢. On the other hand, a developer should not be developing new features when they are oblivious to the unhealthy state of the application.
Let’s look at 7 ways that you can handle an error, before it comes back to bite you.
1. Record your Errors – Use an Exception Handling Library
When developing software, exceptions are a fact-of-life you will need to deal with. Don’t reinvent the wheel, use an existing exception handling library or service.
The best exception handling libraries I’ve come across are:
- Application Insights (recommended)
- Note: for AWS projects, use AWS CloudWatch TODO: Add to Rule
For a more in-depth look at each one, check out SSW Rules – Do you use the best exception
2. Present the User with a Nice Error
Your users should never see the “yellow screen of death” in ASP.NET, or the “unhandled exception” message in a Windows application.
However, as a developer you still want to be able to view the detail of the exception in your local development environment.
SSW have a great guide to setting up a development environment exception pages in ASP.NET Core here: https://www.ssw.com.au/rules/do-you-present-the-user-with-a-nice-error-screen
3. Use a Trace Logging Library
Although commonly done and technically rewarding, writing your own logging infrastructure code is a waste of time. There are so many awesome logging abstractions in .NET Core, .NET 5 , and .NET 6, that you should be using one of them.
These abstractions allow you to:
- Create log entries in a predictable and familiar fashion – you use the same patterns for logging in a Background Service as you would in a Blazor WASM app (just some slightly different bootstrapping 😉)
- Use Dependency Injection – your code should not take a dependency on a particular framework (as they are abstractions)
- Filter output based off severity (Verbose/Debug/Info/Warning/Error) – so you can dial it up or down without changing code
- Have different logs for different components of your application (e.g. a Customer Log and an Order Log)
- Multiple logging sinks – where the logs are written to e.g. log file, database, table storage, or Application Insights
- Supports log message templates allowing logging providers to implement semantic or structured logging
- Can be used with a range of 3rd party logging providers
When your code doesn’t run as planned, if set up correctly, logging can save you hours. I recently watched a great video by Microsoft MVP Jernej “Jk” Kavka, that shows you the best ways to set up your logging. Check it out below – a 20 minute watch, to save a lifetime of pain! 💪🏻
4. Catch and re-throw your exceptions
A good catch and re-throw will make life easier while debugging, a bad catch and re-throw will ruin the exception’s stack trace and make debugging difficult.
- Catch and rethrow where you can usefully add more information, and this saves the dev having to work through the layers to understand the problem
- Never use an empty catch block. Do something in the block or remove it
- Never re-throw exceptions by passing the original exception object. Wrap the exception or use throw; instead.
If you are following the Clean Architecture pattern – catching and rethrowing is useful for preventing your Infrastructure details from leaking into your Application e.g. your code shouldn’t say “we use SQL server”
5. Catch your exceptions precisely
In life when you criticise someone, it’s better to be specific rather than to have a general whinge.
It’s the same in code.
In the try and catch block, if you are catching a general Exception , you will never know where the true problem is. Therefore, catch the specific exception.
6. Don’t Throw an exception using System.Exception
While everyone knows that catch (Exception ex) is bad, it is important to remember that throw new Exception() is worse.
System.Exception is a very extensive class, and it is inherited by all other exception classes. If you throw an exception with the code throw new Exception(), what you need subsequently to handle the exception, will be the infamous catch (Exception ex).
As a standard, you should use an exception class with the name that best describes the exception’s detail. All exception classes in .NET Framework follow this standard very well.
As a result, when you see Exceptions like FileNotFoundException or DivideByZeroException, you know what’s happening just by looking at the exception’s name. The .NET Framework has provided us a comprehensive list of exception classes that we can use. If you really can’t find one that is suitable for the situation, then create your own exception class with the name that best describes the exception e.g.: EmployeeListNotFoundException.
Also, System.ApplicationException should be avoided as well unless it’s an exception related to the application. While it’s acceptable and should be used in certain cases, be aware that using it broadly will be just as bad as ‘throw new Exception()’.
You can see an example of this on SSW Rules: https://www.ssw.com.au/rules/do-you-know-that-you-should-never-throw-an-exception-using-system-exception
7. Use an Analytics framework to help manage exceptions
Finally, the ability to see the overall health performance counters, exceptions, data usages, page hit counts etc. of your application, ensures you are well in control. Developers want to have the necessary information at their hands to action any bugs or performance issues. An analytics framework allows you to do all of that in a consistent and centralized manner.
An analytics framework puts you in control of your application and allows you to do the following:
- Capture, log and action exceptions
- Analyse performance issues and identify bottlenecks
- Track application usage down to individual components
- View and create performance reports
- Analyse user demographics
There are a number of existing Analytics frameworks available on the market, so there is no need to “re-invent the wheel”. Why would you write your own, if someone else has already taken the trouble to do it? We recommend using one of these frameworks or services:
Each one of those frameworks has a fairly extensive set of tools available and are easy to integrate into your application.
Hopefully these 7 tips will help you keep your application healthy! Let me know what you think by commenting below!