Drew BarontiniProduct Director at Differential

Using Variables to Improve Conditionals

Published

January 30, 2020

Reading Time

1 min.

Programming and software development is replete with conditional logic. That is, creating a flow of the program based on certain events. And while this is natural, it can often lead to confusing branches in your code. That's why I like using variables — and the explicit names of those variables — to make conditional logic easier to read, understand, and reason about.

With good variables names in conditionals, reading the code in plain English should yield an understandable phrase.

Let's look at an example.

const getBooks = async () => {
  /* ... */
}
const books = await getBooks()

// EXAMPLE #1
// We want to return `null` if `books` is empty.
if (books.length === 0) return null

// EXAMPLE #2
// The same logic, but with a variable.
const noBooks = books.length === 0

if (noBooks) return null

Reading the code in plain English should yield an understandable phrase.

So how would we read these separate conditionals out loud?

  1. "If the books length is equal to 0, return null."
  2. "If there are no books, return null."

While this is a contrived example — and certainly a less-than-complicated conditional — I believe it effectively illustrates the primary point. The second example reads more naturally, which is going to make it easier to read, understand, and reason about.

Let's look at a different example. This time we want to not only return null if the books are empty, but we want to make sure the current user has access to the "Books" feature. Let's assume each user has an array of features to determine which features they have access to.

// We have a `useCurrentUser()` custom Hook to get the current user.
const currentUser = useCurrentUser();
const getBooks = async () => { /* ... */ }
const books = await getBooks();

// EXAMPLE #1
// We're being fancy and using the optional-chaining
// syntax (in newer versions of JS).
if (books.length === 0 || !currentUser.features?.includes('Books')) return null;

// EXAMPLE #2
const noBooks = books.length === 0;
const userHasAccessToBooks = currentUser.features?.includes('Books'));

if (noBooks || !userHasAccessToBooks) return null;

So how would we read these separate conditionals out loud?

  1. "If the books length is equal to 0, or the current user features doesn't include "Books", return null."
  2. "If there are no books or the user doesn't have access to "Books", return null."

Again, the second conditional feels more natural. And we could take it further, if we wanted.

const noBooks = books.length === 0;
const userHasAccessToBooks = currentUser.features?.includes('Books'));
const shouldNotRenderBooks = noBooks || !userHasAccessToBooks;

if (shouldNotRenderBooks) return null;

"If should not render books, return null". Even more simple. But, as with all things, use your discretion to find the right balance for you and your team.

Additional benefits to the approach

There are additional benefits to explicitly named variables in conditionals:

  • It's easier to log out the value. The value is now assigned to a variable, so console.log()-ing (yes, I'm making it a verb) is easier to work with and debug.
  • We can seamlessly combine it with additional logic. As we see in the second and third examples, adding additional conditions is more trivial when we encapsulate each condition in its own explicitly named variable.
  • Tracking down the conditions via global search is a cinch. With these explicitly named variables, it's a lot easier to do a global search for the exact variable name.