Clean Code in JavaScript
DRY, KISS, YAGNI and SOLID principles.
Clean Code is not just readable and understandable code, but also code that is easy to test, extend and maintain. In JavaScript, it's often difficult to maintain cleanliness, especially when we're looking for quick solutions. In this article, we'll get acquainted with the basic principles that help write better structured code.
These are: DRY, KISS, YAGNI and SOLID principles.
// Bad example
function calculateRectangleArea(width, height) {
return width * height;
}
function calculateRectanglePerimeter(width, height) {
return 2 * width + 2 * height;
}
function describeRectangle(width, height) {
const area = width * height;
const perimeter = 2 * width + 2 * height;
return `Area: ${area}, Perimeter: ${perimeter}`;
}
Good version: we reuse the logic
function calculateRectangleArea(width, height) {
return width * height;
}
function calculateRectanglePerimeter(width, height) {
return 2 * (width + height);
}
function describeRectangle(width, height) {
const area = calculateRectangleArea(width, height);
const perimeter = calculateRectanglePerimeter(width, height);
return `Area: ${area}, Perimeter: ${perimeter}`;
}
Advantages:
// Bad example
function isEven(num) {
return num % 2 === 0 ? true : false;
}
Good version:
function isEven(num) {
return num % 2 === 0;
}
Advantages:
// Bad example - redundant functionality
function sendMessage(user, message, isUrgent = false, sendCopyToAdmin = false) {
// currently there's no need to send copy to admin
if (sendCopyToAdmin) {
// send to admin
}
// send to user
}
Good version:
function sendMessage(user, message) {
// send to user
}
Advantages:
SOLID consists of 5 principles that help write more flexible and extensible code.
// Bad example
class UserManager {
createUser(userData) { /* create user */ }
sendWelcomeEmail(user) { /* send email */ }
}
// Good version
class UserManager {
createUser(userData) { /* create user */ }
}
class EmailService {
sendWelcomeEmail(user) { /* send email */ }
}
Advantages:
// Bad example
function discount(price, type) {
if (type === 'regular') return price;
if (type === 'vip') return price * 0.9;
}
// Good version - extensible classes
class RegularCustomer {
getDiscount(price) {
return price;
}
}
class VipCustomer {
getDiscount(price) {
return price * 0.9;
}
}
Advantages:
// Bad example
class Bird {
fly() {}
}
class Penguin extends Bird {
fly() {
throw new Error("Penguins can't fly!");
}
}
// Good version - different bases
class Bird {}
class FlyingBird extends Bird {
fly() {}
}
class Penguin extends Bird {
swim() {}
}
Advantages:
// Bad example - large interface
class Machine {
print() {}
scan() {}
fax() {}
}
// Good version - specialized interfaces
class Printer {
print() {}
}
class Scanner {
scan() {}
}
Advantages:
// Bad example - hard dependency
class MySQLDatabase {
save(data) {}
}
class UserService {
constructor() {
this.db = new MySQLDatabase();
}
}
// Good version - dependency is passed
class UserService {
constructor(database) {
this.db = database;
}
}
Advantages: