Skip to main content

Writing Clean Code Best Practices From Uncle Bob

· 5 min read
Sivabharathy

In the world of software development, writing clean, readable, and maintainable code is crucial. Uncle Bob’s book, “Clean Code,” provides a comprehensive guide on how developers, especially those less experienced, can achieve this. Below are some key principles and rules from the book, along with examples to illustrate how they can be implemented.

1. No Code Comments

A well-written codebase should be self-explanatory, eliminating the need for comments. Descriptive names for variables, methods, and attributes make the code understandable without additional explanations.

Example:

// Bad practice with comments
// This method checks if the user is active
function chkUsrActv(user) {
return user.status === 'active';
}

// Clean code without comments
function isUserActive(user) {
return user.status === 'active';
}

2. Dead Comments or Code Should Be Deleted

Any piece of code or comment that is not being used should be removed. Version control systems like Git can always retrieve previous versions if needed.

Example:

// Bad practice with dead code
function calculateDiscount(price) {
// var discount = price * 0.1; // Old discount logic
return price * 0.2;
}

// Clean code without dead code
function calculateDiscount(price) {
return price * 0.2;
}

3. Incorrect Behaviour at Boundaries

Unit testing boundaries is crucial to ensure no assumptions are made about their behavior. This ensures robust and reliable code.

Example:

// Example unit test for boundary conditions
test('calculateDiscount should handle zero price', () => {
expect(calculateDiscount(0)).toBe(0);
});

test('calculateDiscount should handle negative price', () => {
expect(calculateDiscount(-100)).toBe(-20);
});

4. Positive Conditionals

Positive conditionals are more readable than negative ones. They reduce cognitive load and make the logic clearer.

Example:

// Bad practice with negative conditional
if (!isUserInactive(user)) {
// Perform action
}

// Clean code with positive conditional
if (isUserActive(user)) {
// Perform action
}

5. Standard Architecture, Coding, and Design Guidelines

Adhering to set standards ensures consistency and quality across the codebase. Tools like linters and formatters can help enforce these guidelines.

Example:

// Using ESLint for standard coding guidelines
{
"extends": "eslint:recommended",
"rules": {
"indent": ["error", 4],
"quotes": ["error", "double"]
}
}

6. Good and Consistent Naming

Variable, method, and class names should be understandable from the caller's perspective and consistently applied throughout the codebase.

Example:

// Bad practice with inconsistent naming
function getUsrInfo(usrId) {
return fetchUserDetails(usrId);
}

// Clean code with consistent naming
function getUserInfo(userId) {
return fetchUserDetails(userId);
}

7. KISS Principle

The "Keep It Simple, Stupid" principle emphasizes simplicity. Avoid unnecessary complexity in design and code.

Example:

// Bad practice with unnecessary complexity
function calculate(a, b, operation) {
if (operation === 'add') {
return a + b;
} else if (operation === 'subtract') {
return a - b;
} else if (operation === 'multiply') {
return a * b;
} else if (operation === 'divide') {
return a / b;
}
}

// Clean code with simplicity
function add(a, b) {
return a + b;
}

function subtract(a, b) {
return a - b;
}

function multiply(a, b) {
return a * b;
}

function divide(a, b) {
return a / b;
}

8. Use Exceptions Instead of Return Codes

Exceptions should be thrown in exceptional cases, not return codes or null values.

Example:

// Bad practice with return codes
function getUserById(userId) {
if (userId <= 0) {
return null;
}
// Retrieve user logic
}

// Clean code with exceptions
function getUserById(userId) {
if (userId <= 0) {
throw new Error("Invalid user ID");
}
// Retrieve user logic
}

9. Make Things Small

Keep class and method sizes manageable. Ideally, methods should be compact, with less than 100 lines of code.

Example:

// Bad practice with large method
function processUserData(user) {
validateUser(user);
sanitizeData(user);
saveToDatabase(user);
sendConfirmationEmail(user);
}

// Clean code with small methods
function processUserData(user) {
validateUser(user);
sanitizeData(user);
saveUser(user);
notifyUser(user);
}

function saveUser(user) {
// Save to database logic
}

function notifyUser(user) {
// Send confirmation email logic
}

10. Boy Scout Rule

The "Boy Scout Rule" advises leaving the code cleaner than you found it. Continuous refactoring should be a habit.

Example:

// Before refactoring
function calculateTotalPrice(items) {
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].price;
}
return total;
}

// After refactoring
function calculateTotalPrice(items) {
return items.reduce((total, item) => total + item.price, 0);
}

Conclusion

Adhering to these principles from "Clean Code" helps in creating a codebase that is easier to read, maintain, and extend. While experienced developers may occasionally break these rules with justifications, for most, especially those less experienced, following these guidelines ensures a higher standard of code quality. Clean code is not just a preference; it’s a professional necessity.