Bijay Das logo

Best Practices for Writing Clean and Maintainable Code

3 min read
Best Practices for Writing Clean and Maintainable Code

In software development, making code work is the start. To build a project that lasts and grows, you need to write code that's clean easy to read, and simple to maintain. This matters whether you're working on a small project or a big company app. Sticking to clean code best practices will help you save time, cut down on bugs, and make teamwork smoother.

This article will look at some key ideas and best practices to write clean code that's easy to maintain, with examples to show how it's done.

Use Meaningful Variable and Function Names

Using clear and descriptive names for variables, functions, and classes stands as one of the most basic practices to write clean code. These names should explain the purpose of the variable or function, which helps the code to explain itself.

Example

// Bad Naming let x = 10; function f(a) { return x * a; }

// Good Naming let basePrice = 10; function calculateTotalPrice(quantity) { return basePrice * quantity; }

In the second example, basePrice and calculateTotalPrice clearly describe what the code is doing, making it easier to understand at a glance.

Follow the Single Responsibility Principle (SRP)

Every function or class should do one job. This makes your code simpler to grasp, check, and keep up. If a function takes on too many tasks, think about splitting it into smaller more specific functions.

# Bad Practice: Multiple Responsibilities in One Function def process_order(order): # Validate the order if not validate_order(order): return "Invalid order" # Calculate the total price total_price = calculate_price(order) # Send confirmation email send_confirmation_email(order) return total_price

# Good Practice: Single Responsibility for Each Function def validate_order(order): # Logic for validating the order pass

def calculate_price(order): # Logic for calculating the total price pass

def send_confirmation_email(order): # Logic for sending a confirmation email pass

def process_order(order): if not validate_order(order): return "Invalid order" total_price = calculate_price(order) send_confirmation_email(order) return total_price

By separating responsibilities, each function is easier to test and modify independently.

Avoid Global Variables

Global variables can lead to unpredictable behavior and make debugging difficult. It's better to limit the scope of variables to where they are needed.

Example:

// Bad Practice: Using Global Variable let discount = 0.1;

function applyDiscount(price) { return price * (1 - discount); }

// Good Practice: Pass Variables as Parameters function applyDiscount(price, discount) { return price * (1 - discount); }

In the second example, discount is passed as a parameter, making the function more reusable and easier to test.

Write DRY (Don't Repeat Yourself) Code

Repeating code can lead to inconsistencies and makes your codebase harder to maintain. Instead, look for opportunities to abstract and reuse code.

# Bad Practice: Repeating Code def calculate_area_of_rectangle(length, width) return length * width end

def calculate_area_of_square(side) return side * side end

# Good Practice: Reuse Code def calculate_area_of_rectangle(length, width) return length * width end

def calculate_area_of_square(side) return calculate_area_of_rectangle(side, side) end

The good practice example reuses the calculate_area_of_rectangle function to calculate the area of a square, reducing redundancy.

Comment Smartly

Comments should be used to explain why certain decisions were made, not to describe what the code is doing. The code itself should be clear enough to explain its purpose.

// Bad Practice: Over-commenting int totalPrice = basePrice * quantity; // Multiply base price by quantity to get total price

// Good Practice: Explain the "Why" int totalPrice = basePrice * quantity; // Adjusted to handle discount cases

Over-commenting can clutter your code and make it harder to read. Instead, focus on explaining complex logic or decisions that may not be immediately obvious.

Keep Functions Small

Functions should be small and focused on a single task. This not only makes your code more readable but also makes it easier to test and debug.

// Bad Practice: Large Function Doing Too Much function processUserRequest($request) { // Validate request if (!validateRequest($request)) { return "Invalid request"; }

// Process data $data = processData($request);

// Save data saveData($data);

// Send response sendResponse($data); }

// Good Practice: Small, Focused Functions function validateRequest($request) { // Validation logic }

function processData($request) { // Processing logic }

function saveData($data) { // Save logic }

function sendResponse($data) { // Response logic }

function processUserRequest($request) { if (!validateRequest($request)) { return "Invalid request"; }

$data = processData($request); saveData($data); sendResponse($data); }

Breaking down large functions into smaller, more manageable ones enhances readability and maintainability.

Use Consistent Formatting

Consistent formatting, such as indentation, spacing, and bracket placement, helps make the code easier to read and maintain. Many development environments have built-in tools or plugins that can automatically format code according to a style guide.

# Bad Practice: Inconsistent Formatting def add(a,b): return a+b

def subtract(a, b): return a - b

# Good Practice: Consistent Formatting def add(a, b): return a + b

def subtract(a, b): return a - b

Consistent formatting makes it easier for teams to work together and reduces errors caused by misaligned code.

Write Tests

Writing tests for your code ensures that it works as expected and allows you to make changes with confidence. Unit tests, integration tests, and end-to-end tests can all contribute to a more robust codebase.

// Example of a Unit Test Using Jest function sum(a, b) { return a + b; }

test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); });

Refactor Regularly

Refactoring involves restructuring existing code without changing its external behavior. This process helps to improve the design, structure, and readability of the code.

// Bad Practice: Duplicated Code public class UserService { public void activateUser(User user) { if (!user.isActive()) { user.setActive(true); saveUser(user); } }

public void deactivateUser(User user) { if (user.isActive()) { user.setActive(false); saveUser(user); } }

private void saveUser(User user) { // Save user to database } }

// Good Practice: Refactor to Remove Duplication public class UserService { public void setActiveStatus(User user, boolean isActive) { if (user.isActive() != isActive) { user.setActive(isActive); saveUser(user); } }

private void saveUser(User user) { // Save user to database } }

Regularly refactoring your code helps to keep it clean and prevents technical debt from accumulating.