It is not enough for code to work. - Robert C. Martin
Having spent hours of time writing, reviewing, and architecting web products, if there is one thing that gives me utmost satisfaction is a well-written code.
Writing clean code, to me, should not be a clichΓ© rather a table stake for any software product. During the initial days of my career, I've been fortunate(and grateful) to be mentored by developers for whom "Clean Code" was a matter of habit. As I grew up the ladder in engineering teams, I felt the necessity to document my learnings to a blog.
In this blog, I have addressed ten ideas that will help you start your journey towards writing clean maintainable code. Since I've spent the majority of my career writing code in Javascript my examples here are in Javascript. The concepts, however, can be applied to any programming language.
1. Meaningful Variable Names
// Don't do this π©
const a = 3.14;`
// Do this π
const PI = 3.14
2. No Magic Numbers or Strings
// Don't do this π©
const circumference = 2*3.14*radius;
const isAdminUser = user.type === "ADMIN";
// Do this π
const PI = 3.14;
const USER_ROLES = {
admin : "ADMIN",
clerk : "CLERK"
}
const circumference = 2*PI*radius;
const isAdminUser = user.type === USER_ROLES.admin;
Why?
- If a magic string is being written in multiple places you have to change all of them.
- More manual work increases chances of typos.
- Magic numbers/Strings are not self-documenting.
3. Avoid Unwanted context
// Don't do this π©
const car = {
carMake: "BMW",
carColor: "Black",
carModel: "X5",
};
// Do this π
const car = {
make: "BMW",
color: "Black",
model: "X5",
};
4. Functions should do one thing
This has to be the most important rule of software engineering. When a function does more than one thing, it is very difficult to write concise test cases for it.
// Don't do this π©
function calculateAndDisplaySum(number1, number2) {
let sum = number1 + number2;
console.log(`Sum is ${sum}`);
}
calculateAndDisplaySum(5, 6);
// Do this π
function calculateSum(number1, number2) {
let sum = number1 + number2;
return sum;
}
function displaySum(number){
console.log(`Sum is ${number}`);
}
const sum = calculateSum(5,6);
displaySum(sum);
5. Less than 2 arguments
When the number of arguments is less than two, it makes easier for writing effective test cases.
// Don't do this π©
function createButton(name, title, disabled, onClick){
//....
}
// Do this π
function createButton({name, title, disabled, onClick}){
//....
}
const addToCartBtn = createButton({
name: "addToCart",
title: "\"Add to Cart\","
disabled: false,
onClick: handleClick,
});
6. No Booleans as function arguments
Flags tell that this function does more than one thing. Functions should do one thing(Refer #4). Split out your functions if they are following different code paths based on a boolean. This helps your code to stick to Single Responsibility Principle.
// Don't do this π©
distance(pointA, pointB, true)
// Do this π
distanceInKms(pointA, pointB);
distanceInMiles(pointA, pointB);
7. Naming functions - Right approach
Function names should say what they do. It is a good idea to set a baseline among your development team members on how to approach this. Once this is done it is easier for anyone in the team to understand what the function is expected to do.
// This fetches data instantaneously.
const getUser = () => {};
// This function sets data to an object.
const setUser = (user) => {};
// This function gets data asynchronously.
const fetchUser = () => {}
// This function helps to render DOM.
const renderUser = () => {}
// This function helps to modify a object.
const updateUser = () => {}
// This function helps to handle event.
const handleUserCardClick = (evt) => {}
// These functions returns booleans to take decisions.
const shouldRenderUser = () => {}
const isAdminUser = () => {}
8. Polymorphism over conditionals
function test (condition) {
if (condition === "A") {
// do something related to "A" here
} else if (condition === "B") {
// do something related to "B" here
} else if (condition === "C") {
// do something related to "C" here
}
}
test('A');
test('B');
test('C');
// Write it this way instead
const A = {
doTheThing: function(){
// code related to "A" here
}
}
const B = {
doTheThing: function(){
// code related to "B" here
}
}
const C = {
doTheThing: function(){
// code related to "C" here
}
}
function test (condition) {
condition.doTheThing();
}
test(A);
test(B);
test(C);
9. Use Promises, not callbacks
Javascript functions are first class citizens, but they are messy as callbacks! They are not clean and cause excessive amount of nesting.
// Don't do this π©
get("https://datasource.url/endpoint"),
(requestErr, response, body) => {
if (requestErr) {
console.error(requestErr);
} else {
writeFile("newfile.html", body, writeErr => {
if (writeErr) {
console.error(writeErr);
} else {
console.log("File written");
}
});
}
}
);
// Do this π
get("https://datasource.url/endpoint"),
.then(body => {
return writeFile("newfile.html", body);
})
.then(() => {
console.log("File written");
})
.catch(err => {
console.error(err);
});
10. Comments
How much comment is too much comment? There are various schools of thoughts for this. I firmly believe in the idea of
"Good code mostly documents itself.".
I follow the principle of only commenting things that have business logic complexity.
Learning to write clean code is a journey and not a destination.
For further reading, do consider reading these books.
Top comments (0)