Secret ingredients to quality software


Rules to Better Error Handling

8 Rules

  1. Do you use the best 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 service is Application Insights for Visual Studio Online, but if you can't use that, then use

    Your users should never see the “yellow screen of death” in ASP.NET, or the “unhandled exception” message in a Windows application. Errors should always be caught and logged – preferably in a SQL database. 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.

    default asp error 500 small
    If you see this, you are doing something wrong!

    At SSW we use Application Insights where possible. If you are still developing Windows applications, then you can still use Application Insights, read here for more details.

    Application Insights will tell you if your application goes down or runs slowly under load. If there are any uncaught exceptions, you’ll be able to drill into the code to pinpoint the problem. You can also find out what your users are doing with the application so that you can tune it to their needs in each development cycle.

    If Application Insights is not available we use Seq when developing web applications. From its page:

    Seq is built for modern structured logging with message templates. Rather than waste time and effort trying to extract data from plain-text logs with fragile log parsing, the properties associated with each log event are captured and sent to Seq in a clean JSON format. Message templates are supported natively by ASP.NET Core, Serilog, NLog, and many other libraries, so your application can use the best available diagnostic logging for your platform.

    Application Insights gives you very useful graphs and analysis which give you a good overview of how things are going. See here for more details. Seq is great for identifying specific issues and how to fix them, but is not as good at letting you see the big picture.

    xn4QHnmBS0Kx39gOv0wM GettingStarted 1
    Seq provides you with plenty of details about what is happening, but if you don't already know what you're looking for, it can be tricky to parse

    Application Insights gives you graphs and analysis that help you find issues, but also lets you drill down to get the details as well

  2. Do you use the best trace logging library?

    Did you know that writing your own logging infrastructure code wastes time? You should use a logging library, and the best logging library is Serilog.

    Serilog is a NuGet package that can be included in any .NET application, is easy to configure, supports many different output targets, has great performance, and allows for runtime changes to the configuration. Serilog also supports full integration with log4net so is easy to use with any older codebase using log4net.

    Serilog supports all the log4net concepts of logging at different levels of importance (e.g. Error, Warning, Information) and having different logs for different components of your application (e.g. a Customer Log and an Order Log).

    Serilog's main advantage over previous loggers is its ability to serialize and log full .net objects rather than just strings. Serilog also has a powerful, nested context system allowing log events to be grouped by request, operation etc.

    Serilog has the concept of "sinks": pluggable modules that can consume, store and/or act on logging events. The most powerful sink is Seq - a web application that allows the user to perform complex search queries on incoming log data in real time.

    Other sinks allow you to:

    • save logs to a file
    • export to log4net (and subsequently use any log4net appender)
    • save to a database
    • export to Application Insights, New Relic, and many other APM platforms

    Many other sinks are available as described here:

    trace logging bad
    Figure: Bad Example - Using Debug or Trace for logging, or writing hard coded mechanisms for logging does not allow you to configure logging at runtime

    trace logging bad 2
    Figure: Bad Example - Roll your own logging components lack functionality, and have not been tested as thoroughly for quality or performance as log4net

    Figure: Good Example - Using serilog allows persisting structured log data to multiple stores, querying that data intelligently and requires less work to install and configure than a roll-you-own logger, and provides many more features

    Figure: Good Example - Seq provides a powerful UI for searching and viewing your structured logs

    Serilog should be added to your project via the NuGet package manager.

    See also Do you use the best middle tier .NET libraries?

  3. Do you catch and re-throw exceptions properly?

    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 {} 
    catch (SomeException) {} 
    catch { throw; } 
    catch (SomeException) { throw; } 

    Bad Example - Never use an empty catch block. Do something in the block or remove it.

    catch (SomeException ex) { throw ex; } 
    catch (SomeException ex) { someMethod(); throw ex; } 

    Bad Example - Never re-throw exceptions by passing the original exception object. Wrap the exception or use throw; instead.

    catch (SomeException ex) 
    catch (SomeException ex) 
         SomeOtherException wrapperEx = new SomeOtherException("This is a wrapper exception", ex);
         throw wrapperEx; 

    Good Example - Good code

  4. Do you catch exceptions precisely?

    In the try and catch block, if you always catch for normal Exception you will never know where the true problem is. When using try you should always expect some exception may happen, so in our code we always catch the specific exceptions.

    catch (Exception ex) 
         return ex.ToString ();

    Bad code – Catching the general Exception

    catch (InvalidOperationException ex) 
         return ex.ToString(); 
    catch (SqlException ex) 
         return ex.ToString(); 

    Good code - Catch with specific Exception

  5. Do you know that you should never throw an exception using System.Exception?

    While everyone knows that 'catch (Exception ex)' is bad, no one has really noticed 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()'.

    We have a program called SSW Code Auditor to check for this rule.

  6. Do you present the user with a nice error screen? (Web Only)

    Your users should never see the “yellow screen of death” in ASP.NET. Errors should be caught, logged and a user-friendly screen displayed to the user.

    This last part is done by specifying the customErrors element in the web.config file.

    This will activate ASP.NET’s built in error page (e.g. MVC’s HandleErrorAttribute filter) which can then be customized to suit your application.

    error screen bad
    Figure: Bad Example – Yellow Screen of Death

    error screen good
    Figure: Good Example - Default ASP.NET MVC custom error page

    However, as a developer you still want to be able to view the detail of the exception in your local development environment. Use the below setting in your Web Application's web.config file to view the yellow screen locally but present a nice error screen to the user.

    14 08 2014 2 47 50 PM compressor
    Figure: Good Example - Don't hide the yellow screen of death in the local environment

  7. Do you use an analytics framework to help manage exceptions?

    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 of it and have all the necessary information at your hands to action any bugs or performance issues. An analytics framework allows you to do all of that in a consistent and centralised 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.

  8. Do you always avoid On Error Resume Next? (VB Only)

    Never use On Error Resume Next in VB (and VB.NET) projects.

    If an error occurred, On Error Resume Next will hide the error and things can go very haywire! In .NET, stop using the On Error syntax and use the try-catch exception syntax for better structural exception handling.

    In VB/VBA you should use On Error Resume Next with line of comment and after an offending line of code there should be statement On Error GoTo 0 to reset Errors collection.

    Private Sub cmdSelect_Click()
        Dim varTemp As Variant
        On Error Resume Next
        varTemp = columnADOX.Properties("RelatedColumn").Value
            ....many lines of code...
        intRoutesPerDay = 2
        End Sub

    Bad Example – Bad code

    Private Sub cmdSelect_Click()
        Dim varTemp As Variant
        On Error Resume Next
        'Sometimes there is no related column value
        varTemp = columnADOX.Properties("RelatedColumn").Value
        On Error GoTo 0
        ....continuing code...
        End Sub

    Good Example – Good code

We open source. This page is on GitHub