What is Clean Code?
Clean code is code that is:
- Readable: Easy to understand at a glance
- Maintainable: Simple to modify and debug
- Reusable: Can be repurposed for different scenarios
- Testable: Easy to write unit tests for
- Scalable: Can grow without becoming complex
1. Variables: The Building Blocks of Clean Code
– Use Meaningful Variable Names
Your variable names should clearly indicate their purpose and context.
// Bad const d = new Date(); let u = getUser(); const arr = ['Apple', 'Banana', 'Orange']; // Good const currentDate = new Date(); let currentUser = getUser(); const fruitList = ['Apple', 'Banana', 'Orange'];
– Use Constants for Fixed Values
When a value won’t change, use const instead of let or var.
// Bad var API_ENDPOINT = 'https://api.example.com'; var MAX_ITEMS = 100; // Good const API_ENDPOINT = 'https://api.example.com'; const MAX_ITEMS = 100;
– Maintain Consistent Naming Convention
Use consistent naming patterns throughout your codebase.
// Bad - Inconsistent naming getUserInfo() getClientData() getCustomerRecord() // Good - Consistent naming getUser() updateUser() deleteUser()
– Use Searchable Names
Make your variables and constants easily searchable.
// Bad setTimeout(doSomething, 86400000); // Good const MILLISECONDS_IN_A_DAY = 86400000; setTimeout(doSomething, MILLISECONDS_IN_A_DAY);
2. Objects: Organizing Data Cleanly
– Use Getters and Setters
Encapsulate object properties using getters and setters.
// Bad
class User {
constructor(name) {
this.name = name;
}
}
// Good
class User {
#name; // Private field
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
setName(name) {
this.#name = name;
}
}
– Implement Private Members
Use private fields and methods to protect object data.
class BankAccount {
#balance = 0; // Private field
deposit(amount) {
if (this.#validateAmount(amount)) {
this.#balance += amount;
}
}
#validateAmount(amount) { // Private method
return amount > 0;
}
}
3. Functions: The Heart of Clean Code
– Keep Functions Small and Focused
Each function should do exactly one thing.
// Bad
function processUserData(user) {
validateUser(user);
updateUserInDatabase(user);
sendWelcomeEmail(user);
updateUIWithUserData(user);
}
// Good
function processUserData(user) {
if (validateUser(user)) {
saveUserData(user);
}
}
function saveUserData(user) {
updateUserInDatabase(user)
.then(sendWelcomeEmail)
.then(updateUIWithUserData);
}
– Limit Function Parameters
Use objects to pass multiple parameters.
// Bad
function createUser(firstName, lastName, email, age, location) {
// ...
}
// Good
function createUser(userConfig) {
const { firstName, lastName, email, age, location } = userConfig;
// ...
}
– Use Descriptive Function Names
Function names should clearly describe what they do.
// Bad
function proc(data) { /* ... */ }
// Good
function processUserPayment(paymentData) { /* ... */ }
4. Comments: When and How to Use Them
– Write Self-Documenting Code
Your code should be clear enough that it doesn’t need extensive comments.
// Bad
// Check if user is adult
if (user.age >= 18) { /* ... */ }
// Good
const isAdult = user.age >= 18;
if (isAdult) { /* ... */ }
– Use Comments for Complex Logic
Comments should explain “why” not “what”.
// Bad
// Iterate through users
users.forEach(user => { /* ... */ });
// Good
// Filter inactive users before sending notifications to avoid
// overwhelming users who haven't logged in for 30+ days
const activeUsers = users.filter(user => user.lastLogin > thirtyDaysAgo);
5. Testing: Ensuring Code Quality
– Write Tests First (TDD)
Consider writing tests before implementing features.
// Example test
describe('User Authentication', () => {
it('should successfully login with valid credentials', () => {
const user = new User('test@example.com', 'password123');
expect(user.login()).toBeTruthy();
});
});
– Test Edge Cases
Always test boundary conditions and error cases.
describe('Array Utility', () => {
it('should handle empty arrays', () => {
expect(processArray([])).toEqual([]);
});
it('should handle null input', () => {
expect(() => processArray(null)).toThrow('Invalid input');
});
});
6. Modern JavaScript Features for Cleaner Code
– Use Optional Chaining
// Bad const streetName = user && user.address && user.address.street; // Good const streetName = user?.address?.street;
– Utilize Destructuring
// Bad
const firstName = user.firstName;
const lastName = user.lastName;
// Good
const { firstName, lastName } = user;
– Implement Default Parameters
// Bad
function greet(name) {
name = name || 'Guest';
return `Hello, ${name}!`;
}
// Good
function greet(name = 'Guest') {
return `Hello, ${name}!`;
}