Skip links

Sane defaults over Exceptions

January 4, 2017 at 7:21 AM - by Freek Lijten - 7 comments

Part of our work is defensive programming. A lot of webdevelopment is centered around taking input from a customer, processing it in some way and returning output. Since the source of input is out of our control we are used to writing all kinds of guards. Is this an integer (in case of a dynamically typed language), is it greater than zero, is it smaller than RANDOM_THRESHOLD, etc.

What I see a lot is people using exceptions or even intentional fatal errors for this. I think the reasoning is that since the application will always generate the correct values, the only thing to worry about is malicious attempts and we can show those people 404's or error pages without feeling remorse.

I didn't think much of this, but I've seen a major drawback lately while working on a site that is a bit bigger than I was used to. With over half a million visitors a week and lots of scrapers, bots and other stuff visiting, these exceptions and fatal errors clog up logging quite a bit. Not to the point that we can't handle the volume, but it generates false positives in monitoring channels and it is something we do not want to act upon anyway.

So while I'm happy to see some defensive programming I would be even happier if exceptional situations would be silently resolved to default situations, simply compare the first and the second example:

    // Great!
    if ($limit <= 0) {
        throw new SorryLimitTooLow();
    }
    if ($offset < 0) {
        throw new SorryOffsetTooLow();
    }

    // Better!
    if ($limit <= 0) {
        $limit = 10;
    }
    if ($offset < 0) {
        $offset = 0;
    }  

If the application makes an error we won't bother the user with it and we will notice during testing (Right?). If the user or a scraper tampers with urls or whatever, we won't bother ourselves with it in our monitoring channels. Everybody wins :)

Share this post!

Comments

  1. Patrick van BergenPatrick van Bergen Wrote on January 5, 2017 at 4:37:15 PM

    Blog \o/

  2. FreekFreek Wrote on January 12, 2017 at 4:04:17 PM

    It's been a busy few months, but I need to put stuff out there more again yeah :p

  3. Fabian SchmenglerFabian Schmengler Wrote on January 13, 2017 at 11:36:53 PM

    "if the application makes an error we won't bother the user with it and we will notice during testing (Right?)" - the reality is, we won't notice, or won't understand the error cause it's hidden. I'm not arguing against hiding recoverable errors from the user, but if they come from a bug in the application, I think they should be logged at least. Validating user input is a different topic and should be handled early.

  4. Algy TaylorAlgy Taylor Wrote on January 18, 2017 at 9:20:27 AM

    I think there's a happy medium to be found. Generally, I throw exceptions 'by default': clearly the user (user may not be a human!) has asked for something which there's no way for me to be able to second-guess what they want.

    But in the case you mentioned (limit & offset, I assume to get N records from a list, eg news items) then you can second guess it.

    Offset = -1; this probably still means "at the start of the list".

    Limit = 0 - this probably means "no limit", although we might say that actually the person wants the maximum number you're willing to let them have (RANDOM_THRESHOLD)

  5. BoydBoyd Wrote on January 18, 2017 at 3:02:12 PM

    One could also just advise against throwing Exceptions on bad user input. You know you can't trust the user, so is it really that 'exceptional' when a user requests your application to do something it can't.

    I wouldn't throw an Exception like in your example in the first place inside an controller or validation method (assuming MVC). But at any level deeper than my controller I certainly wouldn't want to have fallback behavior. If I call a specific class method and pass it the wrong parameters (originated from user input) I would want an Exception to be thrown. Getting clogged up logs from those Exceptions? Then prevent them by falling back to default values inside your controller.

    Let me know what you think about this. I didn't read any details about the context of that fallback behavior.

  6. Freek LijtenFreek Lijten Wrote on January 19, 2017 at 2:43:09 PM

    I agree, it is not exceptional to see users entering incorrect data. So by default an exception is sort of strange. I would still use defaults in the called code and not in the calling code, simply since the calling might be spread over more than one location.

    If you really don't want to see this in your called code, maybe a query object (in case of a repository) or some other abstraction can arrange for the defaults to be set. This way you won't duplicate the defaults all over and it won't pollute the called code.

  7. George HotellingGeorge Hotelling Wrote on February 16, 2017 at 4:09:56 PM

    I agree with Postel's Law, that we should be liberal in what we accept and conservative in what we produce. The challenge is that by failing loudly we can improve the code that calls ours. But I think there is a happy medium.

    PHP has significantly improved assert() in PHP 7. If we sprinkle assert()s liberally through our code, we can verify that the inputs are what we expect. Then, when deploying to production, setting zend.assertions to -1 will skip the assert() call completely so that we don't trigger and can use sane defaults.

    In fact research into assertions have found that they are inversely related to bug density.

Leave a comment!

Italic and bold

*This is italic*, and _so is this_.
**This is bold**, and __so is this__.

Links

This is a link to [Procurios](http://www.procurios.nl).

Lists

A bulleted list can be made with:
- Minus-signs,
+ Add-signs,
* Or an asterisk.

A numbered list can be made with:
1. List item number 1.
2. List item number 2.

Quote

The text below creates a quote:
> This is the first line.
> This is the second line.

Code

A text block with code can be created. Prefix a line with four spaces and a code-block will be made.