JavaScript Anti-patterns

Usually people search for patterns to follow but some searches for the anti-patterns to avoid, In my opinion both are equally important, by knowing them you are leveling up your skills in programming using your favorite tool, in this post I'll get you though them and give you some examples to avoid in your next JavaScript project.

🤔 What are the design patterns?

To understand what are the anti-patterns we need first to define what is a pattern. a pattern is usually a well known solution for a recurring problem, or we can first talk about the concept through a food metaphor.

This plate (for people who don't know) is a street Egyptian food called "Koshary", I picked it because it has quite a lot of ingredients including rice, pasta, hummus, lentil, tomato sauce, and more.

koshary

image source [Wikipedia]

Let's imagine the first person who made this dish, how did he think about it? did he add all together from the beginning? did he try other ingredients that didn't work out? how did he picked each of them? I believe that can be applied to any other food.

Now if you search on the recipe of making Egyptian Koshary you can find it everywhere, that's because some people has made this documented after making several trials, these trials introduced a pattern and this recipe is the design pattern to get the same dish (results) every time.

If we project that on programming, so design patterns are simply good solutions for recurring problems, our ancestor developers made in the past and documented it for us not to repeat same issues.

💭 What are the anti-patterns?

We can imagine now what is an anti-pattern, it is basically also some documents for bad recipes, telling people that we tried this before and here are the results, it was simply a bad idea to do the same again.

same for programming, anti-patterns are simply a bad solution for a recurring problem, people thought about it in the past and documented that their trials ended with failure or bad results.

🧐 You might be thinking, why we might have anti-patterns in JavaScript?

The reasons for having anti-patterns despite of having developer guide for all tools might be one of the following or all of them:

  • Different programming languages paradigm
  • Going with the easy shortcut solutions.
  • Adopting the “for now” phrase (which pile tech debt with bad practices)
  • Lake for experience in the tool used (new developers to JavaScript)
  • Or much more ...

💆‍♂️ Anti-Patterns Examples

Now we understand the terminologies, let's have some Anti-patterns / Bad practices to avoid in you next project or maybe enhance your current project whenever you notice one of them 😉 we will start from 0 as we are developers.

0. Naming convention

Your naming convention needs to be very clear and descriptive, whenever you see a variable or a function name you should be able to guess what to expect as a value coming from there, have a look into the following examples

Bad

// This is a user name
let n = "Medhat Dawoud"

// This is for a year
let yyyy = 2021

// This function should duplicate a number
function dNum(n) {
  return n * 2
}

// This function is checking if 2 numbers are equal
function equal(x, y) {
  return x === y
}

Good

// This is a user name
let userName = "Medhat Dawoud"

// This is for a year
let currentYear = 2021

// This function should duplicate a number
function doubleNumber(num) {
  return num * 2
}

// This function is checking if 2 numbers are equal
function isEqual(x, y) {
  return x === y
}

1. Magic numbers or strings

You might face in the code some magic number or a strings, I call them magic because you or one of the your team mates might be wondering from where does this number or string come, a bit confusing, check the examples below for more clarification:

Bad

// Calculate circle circumference which equal to
// 2 * radius * pi
function circleCircumference(radius) {
  return 2 * radius * 3.14
}

// This is one day in milliseconds
const oneDay = 86400000

// Time to live is 6 weeks
const timeToLive = 42

// This is checking if the user is admin
function isAdmin(name) {
  return name === "Medhat"
}

Good

const PI = 3.14

function circleCircumference(radius) {
  return 2 * radius * PI
}

// This is one day in milliseconds
const oneDay = 24 * 60 * 60 * 1000 // 86400000

// Time to live is 6 weeks
const timeToLiveInDays = 6 * 7 // 42

const ADMIN_NAME = "Medhat"
function isAdmin(name) {
  return name === ADMIN_NAME
}

2. Improper use of Truthy and Falsy values

In JavaScript, Falsy and Truthy values are a bit different than other tools, and by Falsy that means equal to false, and Truthy means when we check it is equal to true, we have about 5 different values which are Falsy in JavaScript which might be wise if you check for each one in the proper way instead of checking in general if they are falsy, check below for best practices.

Bad

function isTruthy(y) {
  return !y ? false : true
}

let x
isTruthy(x) // false

x = 0
isTruthy(x) // false

x = ""
isTruthy(x) // false

x = null
isTruthy(x) // false

x = NaN
isTruthy(x) // false

Good

(A comment in front of each value tells you how to check that value)
function isTruthy(y) {
  return !y ? false : true
}

let x
isTruthy(x) // This is okay to check undefined

x = 0
isTruthy(x) // check -> x !== 0 || x > 0

x = ""
isTruthy(x) // check -> x !== '' || x?.length > 0

x = null
isTruthy(x) // check -> x ?? true : false

x = NaN
isTruthy(x) // check -> isNaN(x)

3. Modify DOM in loop

Modifying DOM is one of the most expensive operations that might happen in the browser, because it cause a reflow/repaint for the page, adding or modifying DOM in a loop is the worst, instead you can use something called DocumentFragment which will be created in memory and cause the reflow only once after the loop as follow:

Bad

// Create 10 elements and push them to document
for (let i = 0; i < 10; i++) {
  const div = document.createElement("div")
  div.textContent = i
  // every loop iteration a new element is created
  // and get added to document node what causes
  // re-paint and re-flow
  document.appendChild(div)
}

Good

// DocumentFragment are saved in memory
// push elements all at once into the document node
const fragment = new DocumentFragment()
for (let i = 0; i < 10; i++) {
  const div = document.createElement("div")
  div.textContent = i
  fragment.appendChild(div)
}
document.appendChild(fragment)

4. New Object in Array.prototype.reduce

The problem here is to try to create a new Object in each round in reduce function, trying to make it immutable however it is creating already a new Object/Array in each route so mutating it is fine otherwise you will end up creating extra Objects in memory.

Bad

const users = [
  { name: "Medhat", admin: true },
  { name: "Adam", admin: false },
  { name: "Karma", admin: true },
]

// spread operator is creating a new Obj
users.reduce(
  (acc, item) => ({
    ...acc,
    [item.name]: item.admin,
  }),
  {}
)

// Expected Output
// {
//   Medhat: true, Adam: false, Karma: true
// }

Good

const users = [
  { name: "Medhat", admin: true },
  { name: "Adam", admin: false },
  { name: "Karma", admin: true },
]

users.reduce((acc, item) => {
  acc[item.name] = item.admin
  return acc
}, {})

// Expected Output
// {
//   Medhat: true, Adam: false, Karma: true
// }

5. Manipulate arguments

Here is a very common issue for beginners, the thing is that non-primitive data types are passing by reference not by value, which means when you make an object let obj = { name: 'Medhat' }; and create another variable to copy that object like let obj2 = obj actually obj2 is pointing at the same object in memory and if you make a change like obj2.name = 'Adam' also obj will have "Adam" as a name.

In javaScript you can access all arguments or a function through an object named arguments, given what we understand above, mutating the arguments object will make a mess, check the example below to understand what I mean

Bad

var makePerson = function(color, name, age) {
  if (arguments.length < 3) {
    color = "green"
    name = arguments[0]
    age = arguments[1]
  }

  return {
    name: name,
    age: age,
    color: color,
  }
}

var person = makePerson("Medhat", 18)
console.log(JSON.stringify(person))
// => {"name":"green","age":"green","color":"green"}

I'd advice not to ever mutate the arguments object, if you want to achieve the above result without a problem you might copy the object using let argCopy = Object.assign({}, arguments); which create a new object with a new reference or by sending the color argument to as a last argument and make it optional.

Conclusion

In this article you should have learnt what are design patterns and what are the anti-patterns, learnt that the Koshary is an awesome food from the Egyptian cuisine 😉 (try it if you didn't yet), learnt 6 different examples of anti-patterns/bad practices to avoid in your next JavaScript project, there are more anti-patterns out there try to find them or tweet one to me on Twitter.

I've talked about this topic before in an online event and here is the presentation, feel free to tweet to me if you have any comment or addition to the info in this article.

Tot ziens 👋