Code Talk Episode 2: Tangled!

by Jeff Langr

June 29, 2022

“Tangled!,” courtesy Алекке Блажин (Pexels)

As they add functionality to existing code, some folks like to weave in new strands of behavior around existing code. The resulting tangled code quickly becomes dramatically harder to follow and change.

The formattedName function in this JavaScript code demonstrates some modest entanglement:

const last = parts => parts[parts.length - 1]
const first = parts => parts[0]
const middle = parts => parts[1]

const formattedName = name => {
  const parts = name.split(' ')
  if (middle(parts).length === 1) {
    return `${last(parts)}, ${first(parts)} ${middle(parts)}`
  }
  else {
    return `${last(parts)}, ${first(parts)} ${middle(parts).slice(0, 1)}.`
  }
}

Here are a couple assertions that characterize what this code accomplishes:

expect(formattedName('Rapunzel Bernice Gothel')).toEqual('Gothel, Rapunzel B.')
expect(formattedName('Harry S Truman')).toEqual('Truman, Harry S')

So where’s the entangling in this example? It occurs where the logic to determine whether or not to append a period to the middle name is mixed with the formatting code.

Likely, the tangler first delivered code that assumed middle names were always abbreviated with a period (e.g. “Bernice” → “B.”). Someone later informed them to support single-letter middle names (yes U.S. President Truman’s full middle name was S).1

As a result of the new requirement, the programmer added special-case code using an if statement: if the middle name has one letter, format the whole name with a middle name that’s not abbreviated. Unfortunately this leaves logic regarding the middle initial in three places: the if conditional, the formatting of the special case, and the formatting of the typical case.

Rather than weaving in strands of code, think instead about organizing code in terms of concepts. A middle initial is a concept. Whether or knot (sorry) it needs a period is a detailed part of that same middle initial concept.

Let’s take a comb to the code. Step one toward fixing it involves creating a concept (function) for middle initial, then using this function to handle the typical (Rapunzel’s) case:

const middleInitial = parts => middle(parts).slice(0, 1) + '.'

const formattedName = name => {
  const parts = name.split(' ')
  if (middle(parts).length === 1) {
    return `${last(parts)}, ${first(parts)} ${middle(parts)}`
  }
  else {
    return `${last(parts)}, ${first(parts)} ${middleInitial(parts)}`
  }
}

Step two involves moving the check regarding Truman’s case (“is it a single letter middle name?”) into the middleInitial concept:

const middleInitial = parts => {
  const middleName = middle(parts)
  if (middleName.length === 1) return middleName
  return middleName.slice(0, 1) + '.'
}

const formattedName = name => {
  const parts = name.split(' ')
  return `${last(parts)}, ${first(parts)} ${middleInitial(parts)}`
}

Having a separate middleInitial function allows you to focus either on (a) how the various name parts combined into a format string or (b) how the middle initial is derived, and not both (a) and (b). The intended outcome of each of middleInitial and formattedName is immediately apparent.

Don’t let your code tangle!

Notes

  1. 1: Purportedly Truman signed his name with a period after an S, but that takes the fun away from this requirement in the coding exercise. Some detail on Truman’s name

Share your comment

Jeff Langr

About the Author

Jeff Langr has been building software for 40 years and writing about it heavily for 20. You can find out more about Jeff, learn from the many helpful articles and books he's written, or read one of his 1000+ combined blog (including Agile in a Flash) and public posts.