Writing Self-Confident Code

A common idiom in ruby is to call a method only if its receiver is not nil:

thing.foo if thing

or:

thing && thing.foo

Various libraries exist for making this a little more convenient. You can use andand, or if you are using Facets you can use ergo. And seriously, you should be using them – they make your code cleaner and more succinct.

But don’t let the existence of these libraries give you the idea that having null-checks throughout your code is OK. It’s not. Pervasive null tests are a code smell.

If you find you are using the elvis operator or its equivalent everywhere, you most likely have a design problem. Is it really OK for that attribute to ever be null? In a lot of cases the existence of a null attribute or association is indicative of an insufficiently specified contract. Maybe you should be setting that attribute to a default value at initialization, or requiring an explicit value for it at initialization. If it really is OK for that attribute to be null, consider whether you should be using a Null Object or some kind of default placeholder object rather than generic nil.

Checking for null is almost always an implementation detail, not a part of the domain you are modeling. As such the null check should be isolated to the boundaries of your code, or eliminated altogether. Code that constantly checks if things are null has an insecurity complex, always second-guessing itself. Make your code self-confident. Eliminate null checks wherever you can.

18 comments

  1. Since I'm working almost exclusively with Rails I find myself do this kind of check mainly in controllers and views. In controllers it's mainly to check nested params and in views whether to output something if there's something to output. The latter is the most annoying case, and yes, it does smell.

    Maybe the solution is to add a presentation layer that take care of this conditional nil check?

    1. In Rails, if I have something that may or may not be displayed I like to wrap that in a helper rather than put a conditional in the view.

      But Rails views are smelly in general, because as the least-OO part of Rails they are the hardest part to apply conventional OO patterns and idioms to.

  2. Interesting point. I would like to offer a couple of additional considerations.
    First, being paranoid (or having an insecurity complex) during development can help prevent code from “working” until the 11th hour. Healthy use of asserts to check for NULL's in this case can be a good thing. Secondly, not all problem domains are the same. In the embedded and/or safety critical space, severe damage can result based on unexpected values propagating through code even during development. By working with the first point, significant and costly damage can be avoided (and, in my case, has been more than once.) So, in my case, I try to balance the compulsive (can you say OCD anyone?) checking against out of bounds values against the impact to performance. Specifically, I use assert() heavily to validate initial code passes in development and then disable the assert() when I judge code to be more stable. Of course, this is always a judgment call for any coder/designer.

    1. Having worked in the embedded space, I'd say that that is where it becomes essential to get rid of this kind of coding insecurity complex. If you're checking for null in domain logic it means you aren't checking for it at the boundaries religiously enough, using preconditions, postconditions, and invariants. Something I've done in the past, if an API I had to to use might conceivably return a null, was to wrap that API in a wrapper of my own that always asserts that the return values are non-null. And then only talk to the API through that wrapper – a software condom of sorts. I'm OK with implicit pervasive null-checking of this nature – it's when the conditionals for null checking are mixed into the conditionals for business logic that the code starts to smell funny.

    2. On a second reading I think we're mostly on the same page. I don't really have a problem with using assert() a lot. assert() isn't a conditional – it says “I never, EVER expect this to be null”. I don't have to write a unit test to test that my code handles the null and non-null cases correctly, because the null case will always crash. What I'm advocating against is code that tries to handle nulls as an expected case.

  3. I've always called the code with tons of null checks “paranoid code.” I like that you've called the opposite “self confident.” That's a good phrase for it.

  4. This has been an issue in the database community for a long time. The essential question is, how do you represent missing information? Take birth date, for example. Do you refuse to allow people without birth dates in your code? Do you give them a default birthdate (??). Or do you recognize that nil is a perfectly valid value for a variable that holds a birth date, and deal with it accordingly?

    1. Mark — representing missing information is an important topic. There's nothing wrong (to my mind) with using 'nil' to represent a missing birth date. The problem is that nil is also a perfectly valid value for 'something went wrong somewhere'. So now we have a value with two meanings. It doesn't matter how you choose to represent a missing value — as long as that representation isn't already in use.

      1. I don't think “something went wrong somewhere” is ever a valid value. 🙂 In fact, I think that's the real usefulness of this article: Don't code defensively, assuming that you might have screwed up somewhere and therefore need to put up safety nets. Just do it right in the first place (and use automated testing to define “right”).

        As for using one representation for two concepts, that's a valid argument, which is behind the anti-NULL-ers like Chris Date. I'm on the other side of that argument (the Codd side) and have no problem with the idea that nil represents a “non-value,” especially in favor of fabrications like 9/9/99. That's what nil was designed to do. I've used languages without it (e.g. C) and I'm glad to have it. But as I indicated above, this isn't an argument that's going to get won any time soon. 🙂

  5. Mark — representing missing information is an important topic. There's nothing wrong (to my mind) with using 'nil' to represent a missing birth date. The problem is that nil is also a perfectly valid value for 'something went wrong somewhere'. So now we have a value with two meanings. It doesn't matter how you choose to represent a missing value — as long as that representation isn't already in use.

  6. I don't think “something went wrong somewhere” is ever a valid value. 🙂 In fact, I think that's the real usefulness of this article: Don't code defensively, assuming that you might have screwed up somewhere and therefore need to put up safety nets. Just do it right in the first place (and use automated testing to define “right”).

    As for using one representation for two concepts, that's a valid argument, which is behind the anti-NULL-ers like Chris Date. I'm on the other side of that argument (the Codd side) and have no problem with the idea that nil represents a “non-value,” especially in favor of fabrications like 9/9/99. That's what nil was designed to do. I've used languages without it (e.g. C) and I'm glad to have it. But as I indicated above, this isn't an argument that's going to get won any time soon. 🙂

Leave a Reply

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