Every time you don’t clean your code, a debugger whispers ‘why?’ in despair

Chen Algrabli
HiredScore Engineering
9 min readMar 2, 2023
Photo by Andrea Piacquadio

If it takes you more time to debug your own code than write it, If you ever faced a code and said “who the hell wrote this piece of human torture?!” only to find out it was you 2 years ago, If you come across pieces of code in your work codebase and think to yourself that there must be a clearer way to write them — This blog is for you!

Clean code is a form of art, and nothing less. Many studies found that clean code can improve efficiency, lead to faster debugging, reduce bug rates, make maintenance easier, and create better collaboration among team members. A study made at Google found “that improvements in code quality cause improvements in individual productivity[1].

I found that clean code is a fascinating aspect of programming, and I invested a lot of time in studying and practicing writing clean code. I found this information in form of blogs, peer reviews, pull requests, and books — and I’m here to share it with you. Uncle Bob (Robert C. Martin) is an American software developer and author who has written extensively on the topic of clean code, one of his most famous books is called “Clean Code” (shocking, I know) describes beautifully a number of the most important principles of Clean Code, some of them, and many others, you will find in this blog.

By the exact word of the magical Uncle Bob, Clean Code is:

“… code that has been taken care of. It reads like a story, has a single flow, and has minimal surprises.
… is easy to understand and easy to change. Clean code can be read, and enhanced by a developer other than its original author.
The goal of clean code is to reduce the cost of change over time.”

Clean code is not about memorizing a set of rules or principles, it takes practice — and with that, some failures as well. It’s about writing code that is direct, clear, easy to understand, maintain or change. It’s about creating code that will reduce the time it takes to understand the logic, and the time it takes to modify it as well. Clean code is equally about making code easier to write as it is to read. Does your code tell a story? Will the flow of your code be clear to the least experienced team member? Would colleagues will need to debug your entire code in order to find the single line that is relevant to their task? Your code should be self-explanatory, if you need to explain it to your colleagues for them to fully understand it, it is not fulfilling its potential.

Hopefully, by the time you’ve got to this line, I convinced you that Clean Code is the 8th wonder of the world, and a skill you want to master. So without further due, here are 9 rules and principles that will help you to clean your code.

And to make it extra fun, we are going to take this piece of nightmare code, which notifies a user about the urgent tasks in its todo list, and step by step — clean the hell out of it:

// Our beautiful todo list
const toDoList = [
{'id': 1,
'description': "Write a blog about clean code",
'isComplete': true,
'dueDate': '07/07/2023',
'listName': 'Personal Goals'
},
{'id': 2,
'description': "Hunt down the person who merged debugger; to master",
'isComplete': false,
'dueDate': '01/01/2023',
'listName': 'Hobbies'
},
{'id': 3,
'description': "Run a marathon",
'isComplete': false,
'dueDate': '05/05/2023',
'listName': 'Personal Goals'
}]

// Not clean, kind of a nightmare example
const notifyUrgentTasks = (toDoList) => {
const currentDate = new Date()
let dueDateForUrgentTasks = new Date()
dueDateForUrgentTasks.setDate(today.getDate() + 1)

const urgentTasks = toDoList.filter(task => {
const dueDate = new Date(task.dueDate)
return dueDate >= currentDate && dueDate < dueDateForUrgentTasks
})

if (urgentTasks.length) {
let message = 'You have the following urgent tasks due tomorrow:\n'
urgentTasks.forEach(task => {
message += `- ${task.description} (ID ${task.id})\n`
})
return message
}
}

#1 SRP — Single Responsibility Principle

By the wise words of Uncle Bob (yes, I’m obsessed, get over it), “Functions should do one thing. They should do it well. They should do it only”.
Every piece of code you write, whether it is a class, module, function, or method, should have a single responsibility, a single reason to be changed. If at any time there is more than one unit of logic to be changed, follow what Ross and Rachel did in season 3, and SPLIT it up.

/* Our notifyUrgentTasks function violates the SRP principle because 
it is responsible for both filtering the toDoList array to find the urgent
tasks and notifying the user of the tasks.
These tasks should ideally be separated into two different functions. */

const getUserUrgentTasks = (toDoList) => {
...
}

const createUserUrgentTasksMessage = (urgentTasks) => {
...
}

const notifyUrgentTasks = (toDoList) => {
const urgentTasks = getUserUrgentTasks(toDoList)
const messageToNotifyUser = createUserUrgentTasksMessage(urgentTasks)
return messageToNotifyUser
}

#2 Write small functions

Passing the mic again to Uncle Bob: “The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that”.
The bigger functions are, the more complicated they are to be modified without hurting their original logic and creating regression.
The smaller functions are, the less specific they are, and the more reusable they become.

/* Even the getUserUrgentTasks function we wrote can be split into smaller functions*/

// Before:
const getUserUrgentTasks = (toDoList) => {
const currentDate = new Date()
let dueDateForUrgentTasks = new Date()
dueDateForUrgentTasks.setDate(today.getDate() + 1)

const urgentTasks = toDoList.filter(task => {
const dueDate = new Date(task.dueDate)
return dueDate >= currentDate && dueDate < dueDateForUrgentTasks
})
}

// After:
const isUrgentTask = (task, currentDate, deadlineDate) => {
const dueDate = new Date(task.dueDate)
return dueDate >= currentDate && dueDate < deadlineDate
}

const getUserUrgentTasks = (toDoList) => {
const currentDate = new Date()
let dueDateForUrgentTasks = new Date()
dueDateForUrgentTasks.setDate(today.getDate() + 1)
return toDoList.filter(task => isUrgentTask(task, currentDate, dueDateForUrgentTasks))
}

#3 DRY — Don’t Repeat Yourself

Don’t repeat yourself, that only looks cute on Groot. The more you repeat yourself in different areas of your codebase, the harder it is to maintain the code and find all of the places that have required change. This principle will make it easier to reuse code, create a clean and organized codebase, and make modifications to the code more easily and efficiently. This is a direct implication of the two previous rules, if you write small enough functions and classes that only have a single responsibility the more generic and reusable they become. Every piece of logic should have one representation within the codebase.

#4 YAGNI — You Ain’t Gonna Need It

The only code that should be written, is the one that is in use. Don’t comment out pieces of the code, don’t add code just for the mere chance you might need it in the future, and don’t add unnecessary functionality. Focus on writing code that is relevant to the current requirements. That way an unnecessary code will not distract you when you are making changes to the code, and you will avoid maintaining unused code.

#5 Express Intent

Here is a list of the hardest skills to master from easiest to hardest: Flying a plane, Quantum engineering, and Naming variables. Using meaningful and descriptive names to express the purpose of your code in a clear and explicit way is one of the most important parts of clean code. It’s actually a superpower. Make it a habit to give long descriptive names to your variable, functions, and classes over short and not-meaningful ones. Meaningful names are a replacement for comments. Names are creating context, express the meaning of a code, and are the most useful tool for making your code tell a story.

/* Our notifyUrgentTasks function tells a story, 
the naming gives us the entire purpose of the function
without even seeing the function's logic */

const notifyUrgentTasks = (toDoList) => {
const urgentTasks = getUserUrgentTasks(toDoList)
const messageToNotifyUser = createUserUrgentTasksMessage(urgentTasks)
return messageToNotifyUser
}

/* What if we had written it that way? */
const notifyUrgent = (list) => {
const filteredTasks = getFilteredTasks(list)
const msg = getMsg(filteredTasks)
return msg
}

/* What is that list we get as a parameter?
Why are we filtering the tasks? by what are we filtering them?
What is the msg variable? an error message?
a message to notify the user about something urgent? */

#6 Avoid Magic Numbers and Strings

“Magic numbers” and “magic strings” are hardcoded values that lack context and explanation. Random values in the code make it harder to debug, make changes, and read the code. They make it difficult to fully understand the meaning and the purpose of those values. Make sure that those special values are given proper descriptive and clear names and are given their own dedicated place in the code instead of appearing randomly between the lines.

/* Explain every number and string in your code, 
and use the naming to give more context about the Unit of measure */
const getUserUrgentTasks = (toDoList) => {
const DAYS_LEFT_UNTIL_URGENT_TASK = 1
const currentDate = new Date()
let dueDateForUrgentTasks= new Date()
dueDateForUrgentTasks.setDate(today.getDate() + DAYS_LEFT_UNTIL_URGENT_TASK)

return toDoList.filter(task => isUrgentTask(task, currentDate, dueDateForUrgentTasks))
}

#7 KISS — Keep It Simple, Stupid

Yeah, it’s a bit aggressive, but you get the idea. Don’t over-complicate the logic, if you are writing the most clever and sophisticated piece of code ever, but you are the only one who understands it, the joke is on you. There should be a balance kept between efficient, generic code and readability. make sure your code tells a story, not a riddle. Take as many words as you need to name a variable, and as many lines, as you need to write clear and clean code, good code is not measured by how short the function is or how much logic you can put into one line.

/* Our innocent getUrgentTasks function can be written that way too, but WHY?!*/

const getUserUrgentTasks = (toDoList) => {
const now = new Date()
let dueDateForUrgentTasks = new Date()
dueDateForUrgentTasks.setDate(today.getDate() + 1)
const dueDateForUrgentTasks = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)

const filteredTasks = toDoList
.filter(task => task.isComplete === false)
.filter(task => {
const dueDate = new Date(task.dueDate)
return dueDate >= now && dueDate < dueDateForUrgentTasks
})
.sort((a, b) => {
if (a.listName < b.listName) {
return -1
}
if (a.listName > b.listName) {
return 1
}
return 0
})
.map(task => {
return {
id: task.id,
description: task.description,
dueDate: task.dueDate,
listName: task.listName,
}
})

return urgentTasks
}

#8 Boy Scout Rule

Leave the code area you are working on in a better state than you received it. If you took a few minutes to search for the meaning of a specific variable through the code, rename it. If you saw the logic you need to write in another place, extract it and reuse it. By constantly improving and cleaning the code, we make it easier to maintain our code base. Refactoring isn’t always about starting from zero, and 6 months project. Constantly update the code to make it clearer, even if it’s not your own code, especially when it’s not your own code. Refactor the refactor, and then refactor it again.

#9 Write Clean Code from the Start

It will make it easier to write your code and complete your task, especially when there is a lot of logic needed. The messier the code you start with, the more chance some of the mess will stay there forever. Doing your best to write clean code from the start will help you develop your clean code muscles, start seeing edge cases while writing the logic, identify pieces of logic that should be extracted into a single reusable unit, and make it easier to stay concise.

In the famous words of John Woods: “Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”

Final words

Clean code has a lot of benefits with scalability, debugging, reducing the number of bugs, and maintaining a codebase in an easier way. But it also got a lot of influence on the way we work, the way we approach problems and issues, about the time we invest in understanding the code we read vs. the time we invest in actually changing it. Code is a beautiful way of telling a story and creating value, it can be evolved and improved endlessly. Take the time to clean the code, whether it’s your code or your colleagues, and actively improve your productivity and dev experience.

And don’t forget, there is a special place in hell saved for people who calls their variables i and j.

References

[1] What Improves Developer Productivity at Google? Code Quality
[2] Clean Code A Handbook of Agile Software Craftsmanship by Robert C. Martin
[3] Maintaining Mental Models: A Study of Developer Work Habits by Thomas D. LaToza, Gina Venolia, and Robert DeLine

--

--

Chen Algrabli
HiredScore Engineering

Full Stack Engineer & Guild Master. Passionate about coding, innovating, and learning.