Anas Aldyab at Pexels

Computer programming is somewhere between an art and a science. Your code must match the syntax that the computer expects and understands, but the style and flow of your program are ultimately up to you.

Most programming problems can solve the same issue in many different ways, and judging one as better than another can be tricky at best, and a waste of time at worst. Many times, things come down to personal preference, and other times one approach is just better than another. This could be for performance, conciseness, or readability.

This post explores two patterns that are more likely to be used by junior developers than senior ones. The following examples are in JavaScript, but the general principles extend to other programming languages as well.

Over-Reliance on if and else Statements

Let’s say we are writing a class used to represent Simpsons characters. The constructor of the class accepts a first name, last name, and occupation for that character.

class Character {
  constructor (firstName, lastName, occupation) {
    this.firstName = firstName
    this.lastName = lastName
    this.occupation = occupation
  }
}
const edna = new Character(
  'Edna',
  'Krabappel',
  'Elementary School Teacher'
)

Now, we want to add a getter property to our class that returns a boolean (true/false), representing whether the character is a member of the Simpson family or not.

Edna Krabappel is not a member of the Simpson family, but Lisa Simpson would be. Here is one way that this is achieved, but it isn’t perfect.

class Character {
  constructor (firstName, lastName, occupation) {
    this.firstName = firstName
    this.lastName = lastName
    this.occupation = occupation
  }
  
  get isSimpson () {
    if (this.lastName === 'Simpson') {
      return true
    } else {
      return false
    }
  }
}
const edna = new Character(
  'Edna',
  'Krabappel',
  'Elementary School Teacher'
)
console.log(edna.isSimpson) // Logs false, as expected

This code works as expected, but is needlessly verbose.

For starters, the else block is unnecessary. If the condition is found to be true, then the function will return a value and terminate — the else alternative will never be reached.

This fact allows us to simplify the method to the following:

get isSimpson () {
  if (this.lastName === 'Simpson') {
    return true
  }
  return false
}

Generally speaking, it’s stylistically preferable to avoid else blocks because it reduces code nesting. Although this isn’t always possible, it often is. If you find yourself doing it often, look back over your code, review it, and determine whether it could be improved.

But even with that improvement, the method is still inefficient and overwrought. Since the getter intends to return a boolean as the output, an if statement isn’t needed at all.

This code does the same job, better:

get isSimpson () {
  return this.lastName === 'Simpson'
}

That is much nicer. Comparison operators are often combined with if statements, but they don’t have to be. Sometimes, it’s better to return a boolean directly. The above block of code is evident in its intent and what it does.

Using Functional Programming in a Nonfunctional Way

A functional approach is frequently preferred because it avoids data mutation and extraneous variables. A procedural approach can be appropriate in certain situations, as well.

While your choice of paradigm may be a matter of taste, misuse of functional programming techniques can identify you as a beginner. To illustrate, here’s an example.

Let’s say that we have an array of Character objects available, and want to use this data to create an array of names.

// An example input array could look like this:
const characters = [
  new Character(
    'Edna',
    'Krabappel',
    'Elementary School Teacher'
  ),
  new Character(
    'Lisa',
    'Simpson',
    'Student'
  ),
  new Character(
    'Moe',
    'Szyslak',
    'Bartender'
  ),  
]
// In that case the output we are looking for would look like this:
[
  'Edna Krabappel',
  'Lisa Simpson',
  'Moe Szyslak'
]

The first step is going to be adding a getter to our Character class that returns the full name of the character:

get fullName () {
  return `${this.firstName} ${this.lastName}`
}

With that available, we can move on to getting an array of full names. Here is a solution that works, but leaves room for improvement:

const names = []
characters.forEach(character => {
  names.push(character.fullName)
})

The above code implements forEach and provides a callback function, but it might as well have been implemented procedurally.

Instead of returning a value, each iteration of the loop mutates the external names variable. A for loop could quickly achieve the same thing:

const names = []
for (let character of characters) {
  names.push(character.fullName)
}

forEach just isn’t the right choice for this. To ensure that the callback function remains “pure,” we should use another array method — let’s try to reduce.

const names = characters.reduce((names, character) => {
  return names.concat(character.fullName)
}, [])

This attempt avoids the problems associated with forEach but still isn’t great.

The problem lies with the verb reduce. As well as preventing externally declared variables and mutation, a critical benefit of functional programming is readability.

A functional method like filter or reduce can make for more expressive and readable code when used correctly.

For example, when a programmer sees that an array is being “filtered,” they can assume that a set of items are being inputted, and only a subset of those items will be outputted. The items that were not outputted were “filtered out.”

Likewise, when a programmer sees an array being “reduced,” they can assume that the function will take the input set and “reduce it” to a more compact output. You might “reduce” a list of test scores to a single average.

This gives readers of your code a helpful hint of what it does. If the array were being operated on procedurally, then readers would need to dig into the code at a lower level to understand what is happening.

This solution is not ideal because the verb reduce does not accurately describe what is happening. Since the goal is to return one output item for each input item, map is a much better choice. It’s also much more concise:

const names = characters.map(character => character.fullName)

Writing code that works is good. However, writing code that is succinct, performant, and readable to others is hard but worth it.

Eliminating redundant if and else conditions and selecting array methods appropriately are a good step towards this worthy goal.

These may seem like small details. They are just one such way that experienced programmers can be distinguished from less experienced ones. Your present and future coworkers will appreciate you. And you, in the future, will also appreciate your efforts.