SortGo back

JavaScript Clean Coding Practices: A Guide to Writing Better Code

/* by Tirth Bodawala - Jun 22, 2023 */

card image

Writing clean, readable, and reusable code is an essential skill for every JavaScript developer. This practice not only makes your code easier to understand and maintain, but it also reduces the time required to onboard new developers. This blog post will guide you through 16 clean coding practices that you can start using today to improve the quality of your JavaScript code.

1. Meaningful Naming

The first step towards writing clean code is to give meaningful names to your variables and functions. The names should clearly communicate the purpose of the variable or function, making your code self-explanatory.

Consider the following JavaScript example:

// Bad
let x = 10;
let y = new Date().getFullYear();
if (x > 30) { /*...*/ }
if (y - x > 1990) { /*...*/ }

// Good
let userAge = 30;
let currentYear = new Date().getFullYear();
if (userAge > 30) { /*...*/ }
if (currentYear - userAge > 1990) { /*...*/ }

In the first example, the variables x and y are cryptic and don't reveal their purpose. The second example uses clear and descriptive variable names, improving the code's readability

2. Positive Conditionals

Avoid negative conditionals where possible. They are often harder to understand than their positive counterparts. Consider the following:

// Bad
if (!userExists(user)) { /*...*/ }

// Good
if (userExists(user)) { /*...*/ }

The second example is easier to understand, isn't it? When you read it out loud, it sounds like English, making your code more intuitive.

3. Single Responsibility Functions

The Single Responsibility Principle (SRP) states that a function should do one thing, and do it well. Functions should be concise and shouldn't exceed an average of 30 lines (excluding comments and white space). If a function is doing more than one thing, consider breaking it into smaller, more manageable functions.

// Bad
function createAndDisplayUser(name, age, address) { /*...*/ }

// Good
function createUser(name, age, address) { /*...*/ }
function displayUser(user) { /*...*/ }

4. Use Default Arguments

Default arguments can make your code cleaner and easier to understand than using short-circuiting or conditionals. They provide default values for undefined arguments. Other falsy values, such as '', "", false, null, 0, and NaN, will not be replaced by a default value.

// Bad
function getUserData(name) {
  const userName = name || "John Doe";
  /*...*/
}

// Good
function getUserData(name = "John Doe") { /*...*/ }

5. Maintain a Single Level of Abstraction

A function should operate at a single level of abstraction. If your function is doing more than that, it's usually an indication that it's doing more than one thing. Dividing a larger function into smaller ones can lead to better reusability and easier testing.

// Bad
function checkSomething(statement) { /*...*/ }

// Good
function checkSomething(statement) {
  const tokens = tokenize(statement);
  const syntaxTree = parse(tokens);
  syntaxTree.forEach(node => { /*...*/ });
}
function tokenize(code) { /*...*/ }
function parse(tokens) { /*...*/ }

6. Don't IgnoreCaught Errors

Caught errors should not be ignored. If an error is captured in a try/catch block, that's an indication that you're expecting a potential error in that code section. Therefore, you should have a plan on what to do when that error occurs. Merely logging the error to the console isn't sufficient as it can easily get lost among other console messages.

// Bad
try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

// Good
try {
  functionThatMightThrow();
} catch (error) {
  notifyUserOfError(error);
  reportErrorToService(error);
}

7. Minimize Comments

While comments can be useful, they should be used sparingly and only when necessary. Good code is self-documenting. If you feel the need to add a comment, consider refactoring your code to make it more clear. The only exception to this rule is when dealing with complex business logic that can't be simplified further.

// Bad
function hashing(data) {
  // The hash
  let hash = 0;
  // Length of string
  const length = data.length;
  // Loop through every character in data
  for (let i = 0; i < length; i++) {
    // Get character code.
    const char = data.charCodeAt(i);
    // Make the hash
    hash = (hash << 5) - hash + char;
    // Convert to 32-bit integer
    hash &= hash;
  }
}

// Good
function hashing(data) {
  let hash = 0;
  const length = data.length;
  for (let i = 0; i < length; i++) {
    const char = data.charCodeAt(i);
    hash = (hash << 5) - hash + char;
    hash &= hash; // Convert to 32-bit integer
  }
}

8. Remove Commented Code

Leaving commented-out code in your codebase can lead to confusion. If the code isn't necessary, remove it. You can always retrieve old code from your version control history if you need it later.

// Bad
// doSomething();

// Good
// Code without unnecessary comments.

9. Import Only What You Need

With ES6, JavaScript introduced destructuring, allowing you to unpack values from arrays or properties from objects into distinct variables. You can use this feature to import only the functions you need from other modules, making your code cleaner and more efficient.

// Bad
import calculate from './calculations';
calculate.add(4,2);
calculate.subtract(4,2);

// Good
import { add, subtract } from './calculations';
add(4,2);
subtract(4,2);

10. Limit Function Arguments

Limit the number of arguments in a function to make testing easier. Ideally, a function should have one to three arguments. More than that could be a sign that your function is doing too much, which violates the Single Responsibility Principle (SRP).

// Bad
function createEmployee(name, age, address, position, salary) { /*...*/ }

// Good
function createEmployee(employeeDetails) { /*...*/ }

11. Use Array Spreads to Copy Arrays

Copying arrays using array spreads is cleaner and more straightforward than using loops. Here's an example:

// Bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
  itemsCopy[i] = items[i];
}

// Good
const itemsCopy = [...items];

This concludes the post on JavaScript clean code practices. I hope you find these tips helpful for writing more readable and maintainable code. Remember, writing code is like an art. It's not just about getting the job done, but also about creating something that's beautiful in its structure and design. So, keep these practices in mind and start transforming your messy code into a masterpiece

Atyantik Technologies: Embracing Clean Code Practices

At Atyantik Technologies, we understand the importance of clean code practices and have embedded them into our daily coding habits. We are not just writing code, we are crafting solutions. Our team takes pride in delivering high-quality code that is easy to read, maintain, and extend.

The clean code practices we've discussed in this post aren't merely abstract principles for us – they are part of our coding DNA. Our developers make it a habit to ensure that every function, variable, and module is named meaningfully, conveying the purpose clearly and eliminating the need for unnecessary comments.

We avoid negative conditionals to make our code more straightforward, and we limit the number of function arguments to uphold the Single Responsibility Principle. Our functions are designed to do one thing and do it well, reducing complexity and making the code more digestible. We also ensure that our functions have appropriate error handling mechanisms in place, preventing them from silently failing and allowing us to react to errors effectively.

We uphold the practice of not leaving commented-out code in our codebase, understanding that version control systems serve the purpose of preserving our code history. We value the power of ES6 features like destructuring and array spreads, using them to make our code more efficient and cleaner.

Every code review at Atyantik Technologies is a learning opportunity. We actively encourage our developers to review each other's code. This not only leads to better code quality but also helps us continually learn and improve our practices.

In conclusion, at Atyantik Technologies, we don't just code – we write clean, efficient, and maintainable code. We believe that these practices are not only beneficial for us as developers, but they also bring immense value to our clients by ensuring that the code we deliver is of the highest quality, easy to understand, and ready for future expansion. We are committed to maintaining these high standards as we continue to deliver exceptional solutions to our clients.