This post was originally published on my own blog, so you can check it out from here
Usually people search for patterns to follow but some of them are searching for the anti-patterns to avoid, In my opinion both are equally important, as by knowing them you are leveling up your skills in programming using a specific tool.
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.
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.
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.
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)
Or much more …
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 😉
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
// 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
// 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
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:
// 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"
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
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
(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)
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:
// 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)
// 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)
The problem here is to try to create a new Object in each round in
reduce function, trying to make it immutable however if 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.
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 //
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 //
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 in pointing at the same object in memory and if you make a change like
obj will have “Adam” as a name.
arguments, given what we understand above, mutating the arguments object will make a mess, check the example below to understand what I mean
var makePerson = function(color, name, age) if (arguments.length < 3) color = "green" name = arguments age = arguments 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 deep-copy the object using
let argCopy = Object.assign(, arguments); or by sending the color argument to as a last argument and make it optional.
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 👋