How to efficiently break your code by using exceptions?

At first I wanted to call today’s post something along the line of “Exceptions should be exceptional” or “keep your exceptions exceptional”. However a quick search on Google made me realize I was not the first to think of this catchy title.

Instead, I decided to go with “How to efficiently break your code by using exceptions?” and this made me think of a different way to present the potential negative effects of overusing exceptions in your codebase. Instead of telling you why you should be careful when using exceptions, I will focus on showing you how to use them “efficiently” in order to break your application in no time. I will be using C# for the examples below, but those could be applied to other languages supporting run-time exceptions, like Java for instance.

A word of notice

most of the examples below are inspired by production code from real applications that I had the chance to work on over the years. I made up the stories around them though, to add a little dramatic touch to the whole. Do try this at home if you want, but please don’t try this at work!

Example 1: Pokemon exception catching (catch ’em all!)

The full code of this example is available in this public gist.

Richard is the sole developer of a small, local vindication company. He was tasked to make the integration between:

  • the in-house application that generates vindication reports for the company’s customer
    and
  • a third-party library, the famous (and imaginary) Vindigator.

One of the main functionalities of Vindigator is the IsCustomerAGoodCustomer method that, thanks to “advanced algorithms”, determines if a given customer is a good customer or a bad customer:

/*
 * 'Vindigator' is an imaginary 3rd-party library that contains
 * a lot of useful (if you're into the vindication business) APIs.
 */
public static class Vindigator
{
    /// <summary>
    /// Return true if the customer is a good customer, false otherwise.
    /// </summary>
    public static bool IsCustomerAGoodCustomer(Customer customer)
    {
        if (customer == null)
            throw new VindigatorInvalidCustomerException("Something is wrong with your customer!");

        if (customer.AmountOwed < 1000)
            return true;

        return false;
    }
}

Vindigator has its own set of exceptions, for instance VindigatorInvalidCustomerException that gets thrown when something is wrong with a given customer. Richard finally finishes the integration, as shown by the GenerateVindicationReport method below:

public static VindicationReport GenerateVindicationReport(Customer customer, ValuationRates rates)
{
    var report = new VindicationReport { Customer = customer };

    try
    {
        report.IsGoodCustomer = Vindigator.IsCustomerAGoodCustomer(report.Customer);

        if (report.IsGoodCustomer)
            report.MoneyOwed = report.Customer.AmountOwed;
        else
            report.MoneyOwed = report.Customer.AmountOwed + report.Customer.AmountOwed * rates.PercentageFee;
    }
    catch (Exception)
    {
        // [Richard] if we get an exception here, it means the customer is null!
        // Seems to happen when the main vindication job gets stuck (damn John!). Return a null report in that case.
        return null;
    }

    return report;
}

During tests, Richard noticed that the scheduled job that calls his GenerateVindicationReport method once a day for each customer is somehow broken. “It must be John’s fault, once again!” says Richard. John is the IT administrator of the company and he was the one who set up the scheduled job. John and Richard generally do not get along very well.

Anyway, John’s job sometimes passes null customers as the first argument of the method. This obviously causes the IsCustomerAGoodCustomer method to throw an exception. Richard knows his topic well and decides to wrap the call in a generic try / catch block, If an exception occurs, meaning the customer passed is null, he will simply return a null report. All is well and the reports are generated perfectly!

A few weeks later though, Richard gets called by his CEO. It appears that the company lost around 70 000 dollars because a lot of reports for indebted customers have been missing! Richard checks the logs of the application, but obviously nothing is there. It is only after 2 stressful days of debugging that Richard realizes that John’s job also occasionally sets the second argument of GenerateVindicationReport to null:

var report = GenerateVindicationReport(customer343, null);

This obviously causes the following line to throw a NullReference exception that gets swallowed by Richard’s generic catch block:

report.MoneyOwed = report.Customer.AmountOwed + report.Customer.AmountOwed * rates.PercentageFee;

This means that during all those weeks, perfectly valid reports for indebted customers were being ignored because of a generic Catch’em all block, leading to a significant loss of money!

This was a very costly and powerful lesson for Richard, who swore not to write such a try / catch block again.

Example 2: short-circuiting exception handling

The full code of this example is available in this public gist.

Months have passed and Richard is now much better at dealing with all types of exceptions, as the following snippet shows:

public static void SetupReportGeneration()
{
    try
    {
        VindicationUtils.CreateVindicationReportDirectory("Customers_Batch1");
        // The rest of the init goes here...
    }
    catch (FolderCouldNotBeCreatedException ex)
    {
        HandleFailureToCreateMainReportDirectory();
        Log(ex);
    }
    catch (Exception ex)
    {
        Log(ex);
    }
}

private static void HandleFailureToCreateMainReportDirectory()
{
    // this method creates a temporary directory to store the reports, in case the main directory couldn't be created
}

private static void Log(Exception ex)
{
    // Log the exception to a file and send an email alert to the admin...
}

All exceptions are logged so that Richard knows exactly what went wrong, and known exceptions are gracefully handled. For example, the custom FolderCouldNotBeCreatedException is being handled by calling the HandleFailureToCreateMainReportDirectory method.

FolderCouldNotBeCreatedException is sometimes thrown by the VIndicationUtils.CreateFolder method shown below:

public static class FileUtils
{
    public static void CreateFolder(string path, string foldername)
    {
        // check if the path is valid
        if (!IsPathValid(path))
            throw new FolderCouldNotBeCreatedException(path + "/" + foldername);

        // The rest of logic to create the folder goes here...
    }
}

The method itself is being used in VindicationUtils.CreateVindicationReportDirectory:

public static class VindicationUtils
{
    public static void CreateVindicationReportDirectory(string foldername)
    {
        FileUtils.CreateFolder(FileUtils.DefaultPath, foldername + "_" + DateTime.Today.ToShortDateString());
        // The rest of the logic to create the report directory goes here ...
    }
}

Everything is working well in production, until Alexandro, the new intern of the company, decides to refactor the code a bit. Alexandro’s current task is to create a web version of the report generator. One of the methods he wants to reuse to this purpose is VindicationUtils.CreateVindicationReportDirectory. However Alexandro quickly notices that it throws FolderCouldNotBeCreatedException from time to time, and remembers the advice of Mr Smith, his university teacher:

“always handle exceptions as close as possible to their source!”

With this precious knowledge in mind, Alexandro quickly refactors the method to this:

public static class VindicationUtils
{
    public static void CreateVindicationReportDirectory(string foldername)
    {
        try
        {
            FileUtils.CreateFolder(FileUtils.DefaultPath, foldername + "_" + DateTime.Today.ToShortDateString());
        }
        catch(FolderCouldNotBeCreatedException)
        {
            throw new InvalidOperationException("Oops! An error occurred during the creation of the report directory.");
        }

        // The rest of the logic to create the report directory goes here ...
    }
}

Alexandro is proud of his contribution! He followed the Boy Scout Rule and made the codebase a bit cleaner by handling the exception close to the source and by throwing a new exception that explicitly says there was an error during the creation of the directory.

By doing that, Alexandro also unknowingly broke another part of the application by short-circuiting Richard’s own exception handling one level above!

However Richard cannot really blame Alexandro for this. Alexandro didn’t introduce any breaking change in the interfaces that would cause the compilation to fail. All the methods have the exact same signature as before. And that’s the dangerous thing about exceptions:

Throwing exceptions will make your code lie about itself! You cannot trust the code to tell you what exception it will throw or handle.

Moral of today’s stories?

I have more examples in mind about how to use exceptions to break your application. Let’s keep them for another post though, as this one is getting pretty long already!

Even though the concept of throwing and catching exceptions is pretty simple to grasp, I consider that proper exception handling is one of the most difficult things to achieve in our profession. Don’t count on the compiler for helping you with it. Only thorough testing and proper team communication will save you here.

I hope that the two examples above are a good reminder that you should always keep on your toes when using exceptions anywhere (!) in your code base.

Cheers!

4 thoughts on “How to efficiently break your code by using exceptions?

  1. Pingback: dotnetomaniak.pl

  2. Dawid Sibiński

    Hey,
    really interesting post about exceptions “flow” 🙂 I’ve always been wondering why in Java there is this “throws” keyword and now I think it is supposed to help with such cases. Maybe it even does?
    I think the other interesting topic in exceptions handling are database transactions (e.g. using TransactionScope and the fact of corrupting the transaction when any exception – even handled – is thrown) – maybe you can also analyze this topic in some of the next posts? 🙂

    Cheers!

    Reply
    1. Youenn Post author

      Thanks Dawid!

      About Java “checked exceptions”, I was wondering the same thing one week ago while preparing this post. I asked one of my experienced Java colleagues about it, and it seems that checked exceptions are not getting much love. The concept seems nice at first sight but it leads to the same type of errors as run-time exceptions. So people usually prefer to use the latter.

      About exception handling in database transactions: this is one of the remaining stories that I have :). You probably remember that I wasted a lot of time on those 😉

      Cheers

      Reply
  3. Pingback: Daj Się Poznać 2017, The End! – Youenn Bouglouan

Leave a Reply

Your email address will not be published. Required fields are marked *