Arrested development: enigmatic Boolean parameters

September 23, 2015

While attempting to read some code recently, I came across something that had me scratching my head: a constructor that had six required Booleans!

Let that sink in for a minute—six required Booleans.

Some may be wondering, "What's the big deal?"

Well, take a look for yourself:

copyOfItems.push(
    new WidgetType(
        "All",
        true,
        false,
        true,
        false,
        false,
        false));

If you're like me, you spend quite a bit of time reading code to understand how it's used and look at examples, rather than inferring how you think it should be used based on the function identity by itself. When I come across code like this, I get irritated that I now have to interrupt my thought process, getting sidetracked by trying to understand something that should be simple and inferred. We're often taught that our code should read like prose and be self-documenting. This type of code violates that principle.

What do those Boolean parameters mean?

Enigmatic Boolean parameters are confusing!

What's an enigmatic Boolean parameter?

For our purposes, let's define an enigmatic Boolean as a Boolean parameter in which there is no context provided for understanding what it represents. It's not clear what it is intended for. At its most basic level, it's a raw Boolean value being passed into a function. When you have enough enigmatic Booleans, you've got a problem. Let's further agree that we will cringe and shake our fists when we come across them.

Who in their right mind would ever add six enigmatic Boolean parameters?

We don't always start off with an unwieldy API. Sometimes, as is the case here, we start of with a single compromise. Here's how that code used to look:

copyOfItems.push(
        new WidgetType("All", true));

This is a good example of the broken windows theory in practice. We started out with only a single enigmatic Boolean and established a precedent that other developers followed.

"Social psychologists and police officers tend to agree that if a window in a building is broken and is left unrepaired, all the rest of the windows will soon be broken .... one unrepaired broken window is a signal that no one cares, and so breaking more windows costs nothing."

—James Q. Wilson and George L. Kelling

What can we do about enigmatic Boolean parameters?

This really depends on the language.

Clarify the context!

There are a few ways of clarifying context. If your language supports named parameters, you can do something like:

copyOfItems.push(
    new WidgetType(
        name="All",
        canCreate=true,
        canDelete=false,
        canSubmit=true,
        canFile=false,
        canReview=false,
        canExport=false));

That's actually much more readable.

What if the language doesn't support named parameters?

It might surprise people to find out that there are many languages that don't actually support named parameters. I know, that's so backwards.

Here's yet another way of clarifying context:

copyOfItems.push(
    new WidgetType(
        name="All",
        CreatePolicy.allowed,
        DeletePolicy.notAllowed,
        SubmitPolicy.allowed,
        FilePolicy.notAllowed,
        ReviewPolicy.notAllowed,
        ExportPolicy.notAllowed));

What if our parameters have default values?

Both of the preceding patterns are still applicable, but there is also a third option that is intended to reduce the number of constructors. This also has a side effect of improving code readability. We can apply a builder pattern to this case.

copyOfItems.push(
     WidgetType
        .create()
        .name("All")
        .canCreate(true)
        .canSubmit(true)
);

class WidgetType
{
    private var _canCreate:Boolean = false;
    private var _canDelete:Boolean = false;
    private var _canSubmit:Boolean = false;
    private var _canFile:Boolean = false;
    private var _canReview:Boolean = false;
    private var _canExport:Boolean = false;
    public static function create():WidgetType
    {
        return new WidgetType();
    }

    public function canCreate(val:Boolean):WidgetType
    {
        _canCreate = val;
        return this;
    }

    public function canDelete(val:Boolean):WidgetType
    {
        _canDelete = val;
        return this;
    }
    // additional code left out for the sake of brevity
}

Are enigmatic Boolean parameters ever a good idea?

There is one case in which an enigmatic Boolean parameter doesn't introduce any additional cognitive overhead, and that is when the name of the method already describes it. In other words,

widget.setActive(false)

Why should we clarify these enigmatic Boolean parameters?

We've established that in terms of cognitive overhead, enigmatic Boolean parameters make our lives as developers more difficult. That alone is a great reason to clarify them, but we also have a couple more considerations:

  • Developers will often model future uses of your API after existing. If existing uses are difficult to understand, future uses are likely to be as well.
  • Enigmatic Booleans diminish our code from being self-documenting and reading like prose.
  • The likelihood of an error or misunderstanding is increased with enigmatic Booleans.

Final verdict: clarify those enigmatic Booleans!