As a newly-joined front-end developer, I was assigned a project that had already been delivered to a client who had accepted an āunnaturalā solution. I used clean coding principles to naturalise the solution, which became a model for future projects at the company. These are the principles I used and the process I followed to implement them.
I was handed an assignment that was a very complex piece of code, with hundreds of lines across at least seven different files. It had a messy codebase that was the result of work from at least six previous developers.
The problem with the unnatural solution
The unnatural solution meant the client could use a feature, but it violated fundamental UX design principles. The user was forced to overthink since its behaviour was completely opposite to the real world. It was a traditional whiteboard addition operation in which the user was instructed to accept the result first rather than at the end:
This was unnatural because the total should appear below rather than above the operation. The unnatural functionality was the result of the efforts of at least six previous developers, most of them juniors without enough experience to design such a complex use case.
Looking at the code was like looking at an alphabet soup: unreadable and beyond complex.
I had no clue where to start or where to go. The one thing that was clear was that the code needed to be fixed, so I started by cleaning it. The plan was simple: No plan at all, but remove dead code as the first step!
Whatās dead code?
Dead code represents unused elements, for example:
- Functions that are only mentioned in their own definitions but nowhere else in the code
- Code that is commented
- Unused variables
- Unnecessary comments
Dead code adds noise to the source code, which leads to the opposite of what weāre trying to achieve. The goal is to write code that is easy to read and understand.
Itās important to do an extensive search through the complete application to look for unused elements before removing dead code. This way, you can be sure that removing things wonāt break the solution.
Removing the dead code took me a few days, and there was no short-term reward for doing it. The other options all involved starting from scratch, but this was risky because the client had already accepted the solution. There was value in the software; I just wanted to tame it.
Whatās clean code?
Code that is easy to read and understand is clean code. From Clean code by Robert C. Martin, āCode is clean if it can be understood easily ā by everyone on the team.ā To me, this means that even people who are not used to working in the language the code is written in can still understand its functionality and read it as if it was composed of phrases in plain English.
Following his definition of clean code, Martin has several principles to follow: Four general principles divided into sub-categories related to scopes of software development. However, there is no need to learn and use them all. To keep it simple, we can apply the four general principles to any scope of software development, so there is no need to learn the big list of subcategories and principles.
The four general principles of clean coding, according to Robert C. Martin, are:
- Follow standard conventions
- Keep it simple
- Boy scout rule
- Find the root cause
Hereās how I used them.
Follow standard conventions
I decided to use camel case when naming variables and focused on being consistent across the source code. I was trying to make our code readable, so naming conventions worked like grammatical rules. You can find more about standard conventions here.
In the example below, the ābeforeā code has no naming conventions while the āafterā code does, making it much easier to read. When trying to read the code before applying the naming conventions, we can easily see in the first line that record
and source
are two different words. However, in the second line, itās pretty hard to notice that recorddest
is the result of joining record
and dest
.
When adopting a naming convention, the reader can trust that words are separated using different cases, and this makes the code easier to read.
Before:
const record.source;
const recorddest;
After:
const recordSource;
const recordDest;
Standard conventions make code easier to read and save you time when writing code. Iāve used camel case as my default naming convention for several years across any situation, and itās become second nature to me when writing code. It feels effortless and is so rewarding because coding and reviewing are easier and quicker. Misunderstandings are also reduced and easier to spot.
Simple code isnāt less code
Simpler is always better. Reduce complexity as much as possible: Code should be simple to read and understand.
Simple code doesnāt necessarily mean short or abbreviated. Our goal is to keep it simple to read, which usually means we need to write a little more. The aim is to be more explicit and detailed, which makes the code easier to understand. This might seem unnecessary, but it is helpful to the reader.
Before:
if (record[i] < 24) {
alert('In order to proceed..');
}
After:
const limitDate = 24;
const currentRecordDate= record[i];
const isInvalid = currentRecordDate < limitDate;
const instructions='In order to proceed..';
if (isInvalid) {
alert(instructions);
}
In the example above, I wrote more to make the code easily readable. More detail means itās easier to read and doesnāt need comments. All the variable names are explicitly self-defined, which isnāt the case with the ābeforeā code, where the word record
is so vague and generalist that it could mean anything if we compare it with the camel-cased phrase currentRecordDate
.
Writing a little more to make the code easier to understand is something I still do. Recently a colleague and I were unsure of our code when programming. Because it wasnāt easily readable, we doubted its correctness despite it being right. A good way to test your codeās simplicity is to read it aloud. If you struggle to read it, then someone else will too. Taking time to add more detail now saves you time and suffering later.
Boy scout rule: Leave the campground cleaner than you found it
For example, fix indentation even if the mess was already there. To fix several simple errors like spacing or unnecessary comments might seem like a lot, but if we fix those details once when we notice them, it will add significant value to the code structure.
Before:
if(somethingIsTrue )
{
doSomethingElse();}
After:
if (somethingIsTrue) {
doSomethingElse();
}
In the ābeforeā example, after the word doSomethingElse
there are several signs with no priority, order, or level of importance. Even when those signs might be super important for the code execution itself, they look misplaced. With the āafterā example, we can see that the signs are present, and it is a lot easier to notice that there is a pattern where every closing sign naturally matches an opening sign. This helps a lot when we need to figure out their function, parity and position to simply pass through them and focus on the method that is being executed.
Always find the root cause
Always look for the root cause of a problem. If weāre getting the same error every time we use a method from a shared library, then it will probably be better if we change the shared library instead of changing the result every time we use it.
When I faced the source code, I felt that the root cause might be related to the dirty and unreadable code. By fixing the root cause, I was able to fix all of the user-side functionality issues.
How cleaning code helped me fix bugs
After removing the dead code, it seemed like a good idea to try to translate this code from ājeringonzaā to English, so I started a renaming journey. I wanted to improve the code's readability later.
When renaming, I followed the first principle and defined the standards to be ruled by:
- camelCase
- long names in variables
- short comments
- functions that only did one thing
Hereās an easy-to-read example of the standards and naming conventions I used:
Before:
const recordSource;
After:
const pricesList;
To the hardest ones:
Before:
function eventListener2(y, x);
After:
function addNewRow(rowObject, positionIndex);
I then followed the second and third principles in order to clean each and every line and make it more readable.
At this point, I began seeing results: The number of lines was reduced at least to half of the original code, and the variables, including the functions, were written in English. This, along with proper indentation, meant the code was finally readable!
The code being readable gave me a huge advantage since it was relatively easy to find absurd sentences in the code with phrases related to the bad output. For example, at some moment drawTotalAdditionResult();
was called before drawElementsToAddition()
.
Cleaning the code enabled me to solve bugs that had been in the backlog for months in just one afternoon!
How clean code makes me a better developer
I now write code thinking about a future me who will sit in front of the computer without and might not remember what seemed so obvious at the time of coding.
Spending time thinking about my code before I write it has saved me time and effort in debugging later on.
Clean code leads to clean ideas, and I would highly recommend incorporating these principles into your coding process. These principles have made me a better developer and allowed me to fix what seemed like an impossible task while adding additional value to our clients.
Resources:
My summary of Clean Code by Robert C. Martin
If you enjoyed this article, you might also like:
Carlos Limonggi is a software developer who likes writing. He finds the process of transforming an abstract idea into sharable words and phrases fascinating.