Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Thinking About Boolean Arguments As A Code-Smell In A Legacy Codebase

By Ben Nadel on
Tags: ColdFusion, Work

To say that Boolean arguments represent some sort of a "code-smell" is not something new or unique. Martin Fowler has a "FlagArgument" article on the topic dating way back to 2011. However, I've been working in a single legacy codebase since about the same time; and I've seen it amass its own share of Boolean arguments, many of which - but not all - do feel quite janky. As such, I wanted to see if I could step-back and try to articulate why some Boolean values are smelly while others are not.

When we talk about a Boolean Argument, what we're talking about is a True/False flag that gets passed into a Function that changes the behavior of that Function in some way. For example, consider a directoryCreate() function in which you can pass a Boolean flag that indicates whether or not the prefix of the given directory should be auto-generated if it doesn't exist:

function directoryCreate(
	required string path,
	optional boolean createPathIfNotExists = false
	) {

	// ....

}

Here, the optional createPathIfNotExists argument determines if parent directories are automatically created as part of the function execution.

So, to be clear, we are not talking about Functions that accept Boolean arguments as part of an Entity definition. Meaning, if you are persisting an entity that has a Boolean property, passing said property to a Function is not in the scope of this conversation.

Furthermore, not all Boolean arguments within the scope of this conversation feel "wrong". For example, in the directoryCreate() code above, I'm perfectly fine with the Boolean argument. Similarly, passing a "recurse" Boolean to a directoryDelete() Function also feels fine to me.

So, what does feel wrong?

Boolean Arguments That Change Return-Value Structures

In the rising tide of GraphQL, this may seem like an antiquated feeling; but, it feels wrong to me when a Boolean argument can change the structure of the Function's return value. For example, imagine a Function that returns a collection of Users and accepts an includeAvatars Boolean argument that conditionally calculates and injects a URL that points to the user's Avatar image.

Why Does This Feel Wrong? To me, this pattern typically represents inappropriate coupling in which the User Interface (UI) needs are "leaking" down into the lower-level control-flows. Presumably, this Function didn't always do this. And then, one day, a developer needed the Avatar in a particular UI; so, instead of writing a more appropriate function, they went down into the call stack and added this behavior specifically for their UI needs.

Boolean Arguments That Change Security Rules

It feels wrong to me when a Boolean argument can bypass security checks. For example, imagine a Function in which an isAdmin Boolean argument can be passed-in in order to get the Function skip any lower-level assertions about what the authenticated user can and cannot do.

Why Does This Feel Wrong? To me, this pattern typically represents a Function that has been repurposed (as opposed to being reused). It means that a Function was designed to do one thing; and now, someone needs it to do something entirely different. But, instead of writing a new Function for the new purpose (and perhaps factoring-out the shared logic), they are just shoe-horning the old Function into the new Context.

In these kinds of cases, there is usually an underlying issue that prevents reuse: a security check being performed too far down within the call stack. If you can move security checks higher-up into the call stack, then lower-level Functions immediately become much more reusable.

Boolean Arguments That Change Workflow Processing

It feels wrong to me when a Boolean argument can change and augment the way something is processed. For example, imagine a Function in which a sendNotifications Boolean argument can be passed-in in order to get a Function to send out email-notifications after some lower-level action is performed.

Why Does This Feel Wrong? To me, this pattern typically represents a Function that has been repurposed (as opposed to being reused). Just as with the "Security Rules" issue above, this usually means that a Function is being shoe-horned into a context for which it wasn't designed.

And, just as with the "Security Rules" issue above, there is usually an underlying issue in which control-flow logic was applied too far down in the call stack. If you can move Notification logic higher-up in the call stack, then lower-level Functions immediately become more reusable.

Boolean Arguments That Change Data Persistence

It feels wrong to me when a Boolean argument can determine whether or not some internal calculation is persisted to a database. For example, imagine a Function in which a doSave Boolean argument can be passed-in in order to get a Function to not only calculate some intermediary result (perhaps the return value of the Function itself) but, then, to also save that result to the database.

Why Does This Feel Wrong? To me, this pattern represents a poor separation of concerns. A Function should persist data; or, calculate data; but probably not both. The decision about whether or not to save the data should be moved up in the call stack.

Boolean Arguments That Are Blindly Passed-Down The Call Stack

It feels wrong to me when a Boolean argument is just blindly passed-down through the call stack. Meaning, one Function accepts a Boolean flag; and then, internally to its own execution, calls another Function and passes that Boolean flag through.

Why Does This Feel Wrong? For starters, I generally have an aversion to Argument Collections that get passed-down through a call stack. But, more than anything, this feels wrong because I think it takes all of the aforementioned issues and just exacerbates them. Not only are we dealing with Boolean flags; but now, we're dealing with additional layers of indirection.

A Missing Orchestration / Workflow Layer

Ultimately, when I look at all of these "code-smells", they all point to one thing: some sort of a missing orchestration layer. The common cause of all these issues is that some sort of decision was being made too far down in the call stack. And, I believe that's because there wasn't a "good place" to make said decision higher-up in the call stack. This is exactly what an orchestration layer is for: making high-level decisions that make lower-level Functions more focused and reusable.

If I can be completely unguarded, not having a consistent orchestration layer is the biggest mistake that I've made in my application development career. And, it is unfortunately, a mistake that me and my team continue to pay for year after year.

A Misunderstanding Of "Code Duplication"

Developers seem to have an inexplicable fear of code duplication. And, I think this fear drives them to try and shoe-horn functions into places that they were not intended to be used. All in an attempt to avoid writing a few lines of code that "look the same".

What we need to avoid is "duplicated business logic" - there's nothing wrong with having code that looks the same as long as it is being controlled by different business rules.


Software development is hard. We're all learning as we go. I've been development my craft quite a while and I am still questioning and re-thinking my decisions all the time. And for now, I'm going to start treating Boolean arguments as a sort of litmus test - an indicator that logic is living in the wrong place. Not always; but, often enough that I think it will be a valuable rule-of-thumb.



Reader Comments

I totally agree with this.

It's like when I see a function with ten Boolean arguments.
I am usually thinking there should be 10 functions there, instead!

This is a useful checklist.

Reply to this Comment

Interesting post - and great examples I agree with. I know I am likely guilty of using boolean arguments as a crutch when I don't have time to do it refactor misshapen classes or call stacks. Great food for thought.

Reply to this Comment

I mean, yeah, we all do this to some degree right? At the end of the day, you gotta ship product; and, we don't always have time to make things the way we want. In fact, just yesterday, I put this comment in one of my CFCs:

<cfscript>
	// NOTE: Honestly, I don't feel great about calling this method since it
	// encapsulates a tremendous amount of logic and is actually quite a
	// rabbit-hole of control-flow (baking-in decisions around WebSocket and
	// Email notifications). That said, it's what we have for now, so it's
	// what I will use for now.
</cfscript>

Ha ha - we do what we gotta do :D

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.