Functions Should Do One Thing


When writing functions, it’s tempting to cram multiple operations into one large function. Sometimes deadlines are tight, fatigue sets in, or laziness creeps up. However, this approach makes your code harder to read, debug, and maintain. A good function should do just one thing—and do it well. If a function performs several tasks, it should be broken into smaller functions, each focused on a single responsibility.

Here’s a JavaScript example inspired by Clean Code Handbook (page 302):

function pay(employees) {
  employees.forEach((employee) => {
    if (employee.isPayday) {
      const pay = employee.salary * employee.hoursWorked;
      employee.bankAccount.deposit(pay);
      console.log(`Paid ${pay} to ${employee.name}`);
    }
  });
}

While the function itself is only 9 lines long, it violates the principle of a single responsibility. When a function is written like this, it makes it more difficult to read, refactor, and test. 

function pay(employees) {
  employees.forEach((employee) => processPayment(employee));
}

function processPayment(employee) {
  if (isPayday(employee)) {
    const pay = calculatePay(employee);
    deliverPay(employee, pay);
    logPayment(employee, pay);
  }
}

function isPayday(employee) {
  return employee.isPayday;
}

function calculatePay(employee) {
  return employee.salary * employee.hoursWorked;
}

function deliverPay(employee, amount) {
  employee.bankAccount.deposit(amount);
}

function logPayment(employee, amount) {
  console.log(`Paid ${amount} to ${employee.name}`);
}

Benefits of Smaller Functions

Breaking down functions into single-responsibility components provides several advantages:

1. Improved Readability

Each function is short, descriptive, and easy to understand. You don’t need to wade through the logic to figure out what calculatePay or deliverPay does. Their purpose is clear from their names.

2. Easier Maintenance

When a requirement changes—say, you need to update how payments are logged—you can modify just the logPayment function without affecting the rest of the system. This reduces the risk of introducing bugs elsewhere.

3. Simpler Testing

Testing smaller functions is much easier than testing a monolithic one. You can write unit tests for isPayday, calculatePay, or logPayment independently, ensuring that each part of your application works correctly.

4. Separation of Concerns

By dividing responsibilities, you make your code modular. This modularity allows you to isolate logic, leading to better architecture and long-term scalability.

Conclusion

Clean, maintainable code is not about adding more lines; it’s about structuring those lines intelligently. Writing functions that do one thing is a fundamental principle of clean code, and it pays off in readability, maintainability, and testability.

Next time you’re tempted to write an all-in-one function, pause and ask yourself: Can this be broken into smaller functions? The answer will likely lead to cleaner, more efficient code.

Happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *