13 Essential Tips for Writing Clean Code: A Guide to Better Code Quality 2024

Md. Saddam Hossain
6 min readFeb 21, 2024

--

13 Essential Tips for Writing Clean Code: A Guide to Better Code Quality 2024

Writing clean code is an essential aspect of software development. Clean code not only makes your codebase easier to understand and maintain but also enhances collaboration among team members. In this article, we’ll delve into 13 key tips for writing clean code, along with examples and explanations for each.

1. Avoid using magic strings and numbers

Magic strings and numbers are hard-coded values that lack context and make code difficult to understand and maintain. Instead, use constants or enums to define these values.

// Bad
function getDayOfWeek() {
return 'Monday';
}

// Good
const DAYS_OF_WEEK = {
MONDAY: 'Monday',
TUESDAY: 'Tuesday',
// Add other days...
};

function getDayOfWeek() {
return DAYS_OF_WEEK.MONDAY;
}

2. Functions do exactly one thing (single responsibility)

Each function should have a clear and single purpose, making it easier to understand, test, and reuse. If a function performs multiple tasks, consider refactoring it into smaller, focused functions.

// Bad
function calculateTotalPrice(item) {
// Calculate price
// Apply discounts
// Format currency
}

// Good
function calculatePrice(item) {
// Calculate price
}

function applyDiscount(price) {
// Apply discounts
}

function formatCurrency(amount) {
// Format currency
}

3. Separation of concern

Separate different concerns of your application into distinct modules, classes, or functions. This improves code readability and maintainability.

// Separation of Concerns (SoC)
// Suppose we have an e-commerce application. We'll separate concerns related to user interface and business logic.

// User Interface Concerns
function displayOrderSummary(order) {
// Code to display order summary to the user
}

function promptUserForPayment() {
// Code to prompt the user for payment
}

// Business Logic Concerns
function calculateTotalPrice(order) {
let totalPrice = 0;
order.items.forEach(item => {
totalPrice += item.price * item.quantity;
});
return totalPrice;
}

function processOrder(order) {
// Code to process the order
let totalPrice = calculateTotalPrice(order);
// Additional business logic...
}

4. Naming things properly

Use descriptive and meaningful names for variables, functions, classes, and modules. Clear and concise naming improves code readability and understanding. Delve deeper into my detailed article on Mastering Clean Code Principles: The Art of Naming Variables and Functions, focusing specifically on the intricate art of naming variables and functions.

// Bad
const d = 5; // unclear variable name

// Good
const daysInWeek = 7; // descriptive variable name

5. Avoid too many arguments in the function (not more than 3)

Limit the number of arguments passed to a function to improve readability and maintainability. If a function requires more than three arguments, consider refactoring it.

// Bad
function createProduct(name, price, category, description, manufacturer) {
// Function body
}

// Good
function createProduct(productDetails) {
// Function body
}

6. Avoid Using Flags as Function Parameters

Avoid using boolean flags as function parameters, as they can make the function behavior unclear. Instead, consider breaking the function into smaller, specialized functions.

// Bad
function fetchData(url, useCache) {
if (useCache) {
// Fetch data from cache
} else {
// Fetch data from server
}
}

// Good
function fetchFromCache(url) {
// Fetch data from cache
}

function fetchDataFromServer(url) {
// Fetch data from server
}

7. Use Guard Clauses (Fail Fast)

Use guard clauses to handle exceptional cases at the beginning of a function, improving code readability and reducing nested conditionals.

// Bad: Without Guard Clauses
function processOrder(order) {
// Check if order is valid
if (!order) {
// Handle invalid order
console.error('Invalid order! Aborting order processing...');
return;
}

// Perform various checks and operations
if (order.status === 'pending') {
// Process pending order
// More logic...
} else if (order.status === 'shipped') {
// Process shipped order
// More logic...
} else {
// Handle unsupported order status
console.error('Unsupported order status! Aborting order processing...');
return;
}

// Final processing steps
// More logic...
}

// Good: With Guard Clauses (Fail Fast)
function processOrder(order) {
// Guard clause to check if order is valid
if (!order) {
console.error('Invalid order! Aborting order processing...');
return;
}

// Guard clause to handle unsupported order status
if (order.status !== 'pending' && order.status !== 'shipped') {
console.error('Unsupported order status! Aborting order processing...');
return;
}

// Process pending order
if (order.status === 'pending') {
// Process pending order
// More logic...
return;
}

// Process shipped order
// More logic...
}

8. Small Method/Component Size

Keep your functions and components small and focused. Small methods are easier to understand, test, and maintain. If a method becomes too large, consider breaking it into smaller, more manageable parts.

// Bad
function processOrder(order) {
// 100 lines of code
}

// Good
function validateOrder(order) {
// 10 lines of code
}

function calculateTotal(order) {
// 10 lines of code
}

function applyDiscounts(order) {
// 10 lines of code
}

9. Avoid Duplicate and Redundant code

Identify and eliminate duplicate or redundant code to improve code maintainability and reduce the risk of bugs. Extract common functionality into reusable functions or classes.

// Bad: Duplicate and Redundant Code
function calculatePrice(item) {
let price = 0;

if (item.category === 'electronics') {
price = item.basePrice * 1.2; // Apply electronics tax
} else if (item.category === 'clothing') {
price = item.basePrice * 1.1; // Apply clothing tax
} else {
price = item.basePrice; // No tax for other categories
}

// Calculate discount
if (item.quantity > 10) {
price -= item.quantity * 0.5; // Apply bulk discount
}

return price;
}

// Good: Avoiding Duplicate and Redundant Code
function calculatePrice(item) {
let price = item.basePrice;

// Apply tax based on category
const categoryTax = {
'electronics': 0.2,
'clothing': 0.1,
// Add more categories and their tax rates as needed
};

if (categoryTax.hasOwnProperty(item.category)) {
price *= 1 + categoryTax[item.category];
}

// Apply bulk discount
const bulkDiscountThreshold = 10;
const bulkDiscountRate = 0.5;
if (item.quantity > bulkDiscountThreshold) {
price -= item.quantity * bulkDiscountRate;
}

return price;
}

10. Consistent Code Formatting

Adhere to a consistent code formatting style throughout your project. Consistent formatting improves readability and makes it easier for developers to understand and contribute to the codebase. Consider using linters and code formatters to enforce consistent formatting.

// Bad
function calculatePrice(item){
// Function body
}

// Good
function calculatePrice(item) {
// Function body
}

11. Use JSDoc comments

Use JSDoc comments to document your code, including function signatures, parameters, return types, and descriptions. Well-documented code improves understandability and helps other developers to use your code effectively.

/**
* Calculates the area of a rectangle.
* @param {number} length - The length of the rectangle.
* @param {number} width - The width of the rectangle.
* @returns {number} The area of the rectangle.
*/
function calculateArea(length, width) {
return length * width;
}

12. Test-Driven Development (TDD)

Practice Test-Driven Development (TDD) to write clean, maintainable code with fewer defects. Writing tests before implementing code helps to clarify requirements and design, leading to better code structure.

// Test case for calculateDiscount function
describe('calculateDiscount', () => {
it('should calculate percentage discount correctly', () => {
const product = { price: 100 };
const discount = calculateDiscount(product, 'percentage');
expect(discount).toBe(10);
});
it('should calculate fixed discount correctly', () => {
const product = { price: 100 };
const discount = calculateDiscount(product, 'fixed');
expect(discount).toBe(90);
});
});

13. Error Handling

Implement proper error handling mechanisms to handle exceptions and errors gracefully. Use try-catch blocks to catch and handle exceptions, and provide meaningful error messages to aid debugging.

function divide(dividend, divisor) {
if (divisor === 0) {
throw new Error('Divisor cannot be zero');
}
return dividend / divisor;
}

try {
const result = divide(10, 0);
console.log(result);
} catch (error) {
console.error('An error occurred:', error.message);
}

Conclusion

In summary, adhering to clean code principles, such as eliminating duplicate and redundant code, greatly enhances software readability and maintainability. By refactoring code to remove duplication and streamline logic, developers create more concise, modular, and understandable codebases. This approach improves productivity, reduces errors, and enables easier maintenance and extension of the software over time. Ultimately, embracing clean code principles leads to higher-quality software and greater satisfaction for both developers and end-users.

--

--