What is Clean Code?
This term might have different meaning for developers. Although there are several best practices you can follow but there is no exact definition for clean code.
But for what I have learned and read, this is what a clean code is,
Clean code is code that is easy to understand, straightforward and consistent, therefore it would be reusable and refactorable.
This is important because as a developer, most of the time we will collaborate with other developers so we need to make sure that our code can be understood easily by everyone in team.
Now, let's take a look on what are the things we must do to write clean code in Javascript by writing examples code related to a Restaurant application.
1. Variables
- Use meaningful and self descriptive names.
⛔️
let rn = 'Bambang Restaurant';
✅
let restaurantName = 'Bambang Restaurant';
- Usually we use camelCase for variables and functions, and PascalCase for class. Sometimes you can also find variables in UPPERCASE, which means the variable is a constant.
⛔️
let IsMENUAvAiLaBle = true;
✅
let isMenuAvailable = true;
2. Function
- Just like variables, function should have self descriptive name.
⛔️
function addToList(list, menu) {
// ...
}
addToList('Indonesian', 'Nasi Goreng');
✅
function addMenuToList(menu, list) {
// ...
}
addMenuToList('Nasi Goreng', 'Indonesian');
- Ideally, function should only have two or fewer parameters. If you have more than two, you should consider using object.
⛔️
function createMenu(title, description, price, isAvailable) {
// ...
}
createMenu('Rendang', 'Most delicious dish in the world', 50000, true);
✅
function createMenu({title, description, price, isAvailable}) {
// ...
}
createMenu({
title: 'Rendang',
description: 'Most delicious dish in the world',
price: 50000,
isAvailable: true
});
- Function should only do one thing.
⛔️
function advertiseMenus(menus) {
menus.forEach(menu => {
const menuList = data.find(menu);
if(menuList.isOpen()){
advertise(menu);
}
})
}
✅
function advertiseAvailableMenus(menus) {
menus.filter(isAvailableMenu).forEach(advertise);
}
function isAvailableMenu(menu) {
const menuList = data.find(menu);
return menuList.isOpen();
}
- Set default object with
Object.assign()
.
⛔️
const restaurantSettings = {
name: 'Bambang Restaurant',
details: null,
category: ['Fine Dining']
}
function createRestaurant(settings) {
settings.name = settings.name || 'Lorem Ipsum Restaurant';
settings.details = settings.details || 'Lorem ipsum dolor sit amet.'
settings.category = settings.category || ['Casual Dining']
settings.isOpen = settings.isOpen || false
}
createRestaurant(restaurantSettings);
✅
const restaurantSettings = {
name: 'Bambang Restaurant',
details: 'All you can eat and drink',
category: ['Fine Dining']
}
function createRestaurant(settings) {
let finalSettings = Object.assign({
name: 'Lorem Ipsum Restaurant',
details: 'Lorem ipsum dolor sit amet.',
category: ['Casual Dining']
isOpen: false
}, settings);
return finalSettings;
}
createRestaurant(restaurantSettings);
3. Conditional
- Encapsulate conditionals.
⛔️
if (menu.discountPrice && paymentMethod.isAvailable) {
// ...
}
✅
function shouldShowPromoRibbon(menu, paymentMethod) {
return menu.discountPrice && paymentMethod.isAvailable;
}
if (shouldShowPromoRibbon(menuInstance, paymentMethodInstance)) {
// ...
}
- Avoid negative conditionals.
⛔️
function isRestaurantNotOpen(restaurant) {
// ...
}
if(!isRestaurantNotOpen(restaurant)) {
// ...
}
✅
function isRestaurantOpen(restaurant) {
// ...
}
if(isRestaurantOpen(restaurant)) {
// ...
}
- Avoid conditional whenever possible. Although this sounds very difficult, you should prefer polymorphism and inheritance over conditional.
⛔️
class Restaurant {
// ...
getStandardOperationTime() {
switch (this.type) {
case 'Cafe':
return this.getStandardOperationTime('Cafe');
case 'FastFood':
return this.getStandardOperationTime('FastFood');
case 'Bar':
return this.getStandardOperationTime('Bar');
}
}
}
✅
class Restaurant {
// ...
}
class Cafe extends Restaurant {
// ...
getStandardOperationTime() {
return this.standardOperationTime;
}
}
class FastFood extends Restaurant {
// ...
getStandardOperationTime() {
return this.standardOperationTime;
}
}
class Bar extends Restaurant {
// ...
getStandardOperationTime() {
return this.standardOperationTime;
}
}
4. Classes
- Prefer ES6 classes over ES5 plain function.
⛔️
const Restaurant = function(name) {
if (!(this instanceof Restaurant)) {
throw new Error("Instantiate Restaurant with `new` keyword");
}
this.name = name;
};
Restaurant.prototype.getSize = function getOperationTime() {
// ...
};
const Cafe = function(name, location) {
if (!(this instanceof Cafe)) {
throw new Error("Instantiate Cafe with `new` keyword");
}
Restaurant.call(this, name);
this.location = location;
};
Cafe.prototype = Object.create(Restaurant.prototype);
Cafe.prototype.constructor = Cafe;
Cafe.prototype.popularDishes = function popularDishes() {
// ...
};
✅
class Restaurant {
constructor(name) {
this.name = name;
}
getOperationTime() {
// ...
}
}
class Cafe extends Restaurant {
constructor(name, location) {
super(name);
this.location = location;
}
getPopularDishes() {
// ...
}
}
- Use method chaining.
⛔️
class Restaurant {
constructor(name) {
this.name = name;
}
setLocation(location) {
this.location = location;
}
setCategory(category) {
this.category = category;
}
save() {
console.log(this.name, this.location, this.category);
}
}
const restaurant = new Restaurant('FastFood');
product.setName('Fast Fried Chicken');
product.setLocation('Bogor');
product.save();
✅
class Restaurant {
constructor(name) {
this.name = name;
}
setName(name) {
this.name = name;
// Return this for chaining
return this;
}
setLocation(location) {
this.location = location;
// Return this for chaining
return this;
}
save() {
console.log(this.name, this.location, this.category);
// Return this for chaining
return this;
}
}
const restaurant = new Restaurant('Cafe')
.setName('Kopi Kopian')
.setLocation('Bandung')
.save();
- You should prefer composition over inheritance whenever you can. Quoting from clean-code-javascript by Ryan McDermott, here is a decent list of when inheritance makes more sense than composition:
- Your inheritance represents an "is-a" relationship and not a "has-a" relationship (Human->Animal vs. User->UserDetails).
- You can reuse code from the base classes (Humans can move like all animals).
- You want to make global changes to derived classes by changing a base class. (Change the caloric expenditure of all animals when they move).
5. Javascript Style Guide
There are several principles or guides on how to write Javascript you and your team can follow.
6. Code Formatter and Linter
You can use a code formatter tool called Prettier to help you auto format your code based on your team's preference, which you can also integrate with linter, such as ESlint for bugs prevention.
Key Takeaways
Consistent on what you and your team has decided. And just like any other programming language, keep your code DRY (Don't Repeat Yourself) and KISS (Keep It Simple Stupid).
Reference:
- https://github.com/ryanmcdermott/clean-code-javascript
- https://medium.com/nerd-for-tech/javascript-guidelines-for-writing-clean-code-687bbe86ef3
- https://www.robinwieruch.de/javascript-naming-conventions
- https://javascript.plainenglish.io/javascript-clean-code-best-practices-461c24c53cae
Thanks for reading!
If you want to know more on CSS clean code, you can head over to this article
Would really appreciate if you could share your thoughts and feedback in the comment ✨
Top comments (0)