In the chapter “Naming Things” of my book about “Agile Engineering Practices”, I wrote that you should name intermediate results (I will link the whole sample chapter at the end of this blog post).

But sometimes, it’s not that easy…

Consider this piece of code. Do you see any quality issue here?

if(content.length > 0 && content[content.length - 1].type === 'LineBreak') {
    //...
}

Comparisons in the condition do not reveal intent, are not from the domain and are too low-level.

Reading this piece of code is harder than it should be because it mixes different levels of abstraction: The low-level handling of the length and the higher-level checking whether the last entry was a LineBreak.

Let’s try to name the intermediate results…

const alreadyHasContent = content.length > 0
const previousLineEndedWithBreak = content[content.length - 1].type === 'LineBreak'

if(alreadyHasContent && previousLineEndedWithBreak) {
    //...
}

Better, but… Oh no, the tests are breaking! Can you spot why?

One can only access the last element of an array if the array is not empty. In the first piece of code, the shortcut “&&” operator prevented us from accessing element when the array was empty.

But now, after naming the intermediate results, that code will always run. And it will cause an error when the content array is empty. What now?

With closures, I can achieve both:

const alreadyHasContent = () => content.length > 0
const previousLineEndedWithBreak = () => content[content.length - 1].type === 'LineBreak'

if(alreadyHasContent() && previousLineEndedWithBreak()) {
    //...
}

Now the tests are green because the function previousLineEndedWithBreak will never be called when alreadyHasContent returns false.

By using closures, I was able to name the intermediate results, make the code take advantage of the shortcut && operator and keep the code reasonably short.