DEV Community

Cover image for JavaScript Clean Code: The Ultimate Guide for Developers
Alvison Hunter Arnuero | Front-End Web Developer
Alvison Hunter Arnuero | Front-End Web Developer

Posted on • Edited on

JavaScript Clean Code: The Ultimate Guide for Developers

Howdy, Fellas!

In the realm of web development, we often encounter numerous JavaScript resources and tutorials, each offering a unique approach to coding. While these resources provide valuable insights, they can sometimes introduce challenges in code comprehension due to inconsistent naming conventions and unclear structures.

In this comprehensive guide, we'll dive into the art of crafting clean and maintainable JavaScript and TypeScript code. We'll cover best practices for functions, variables, and methods, ensuring your code is not only efficient but also easily understood by both you and your collaborating developers. Let's enhance your coding skills and optimize your development process for a more efficient and enjoyable coding experience.


Variable Names: As a web developer, one of the fundamental principles for writing clean and maintainable code is to use meaningful variable and function names. Code readability is crucial for collaboration and for your own future reference, and using descriptive names that accurately convey the purpose and functionality of your variables and functions is a significant step in achieving this goal.

Avoid the temptation to use single-letter variable names or cryptic abbreviations that can make your code difficult to read and understand. When it comes to variable names, you should always aim for clarity and descriptiveness.

Let's explore this concept further using JavaScript and TypeScript:

// Poor approach
var c;
var d;
var cd;
var u;

// Best approach
let counter = 0;
let date = null;
let currentDate = null;
let user = "";

Enter fullscreen mode Exit fullscreen mode

Boolean Variables: When working with boolean variables in JavaScript and TypeScript, it's essential to maintain clarity in your code. Instead of using single letters or ambiguous words, consider representing boolean variables as questions or statements that clearly convey their purpose. TypeScript adds a powerful layer of static typing to JavaScript, further enhancing code readability and maintainability.

Here's an example of how to write clean and clear boolean variables in TypeScript:

var v=false;
var w=false;
var c=false;
var l=false;

// Best approach for this
let isValid: boolean = false;
let isWritable: boolean = false;
let isCleared: boolean = false;
let isLoading: boolean = false;

Enter fullscreen mode Exit fullscreen mode

Numeric Variables: When dealing with numeric variables in your JavaScript and TypeScript code, it's crucial to use meaningful and descriptive variable names. This practice improves code readability and maintenance, making it easier for you and your team to understand the purpose of these variables. Instead of using cryptic variable names like bMax, bMin, and bTot, opt for names that clearly convey their intent. Additionally, leverage TypeScript's type system to provide type safety, which helps catch potential bugs during development.

Here's an improved example using TypeScript:

var bMax=false;
var bMin=false;
var bTot=false;

// Best approach for this case
let booksMaximum: number = 50;
let booksMinimum: number = 1;
let booksTotal: number = 51;

Enter fullscreen mode Exit fullscreen mode

Array Names: When working with arrays or lists in JavaScript and TypeScript, it's essential to choose meaningful and descriptive names for your variables. Consider using camelCase for naming conventions and provide clear names that indicate the purpose of the array. Here's how you can improve your code with TypeScript:

// Poor naming conventions and no type information
var f = ["Mango", "Papaya", "Apple", "Banana"];
var c = ["Mazda", "Toyota", "Nissan", "Ford"];
var m = ["Fish", "Pork", "Beef", "Chicken"];

// Best approach for the case of lists or arrays
let fruits: string[] = ["Mango", "Papaya", "Apple", "Banana"];
let carBrands: string[] = ["Mazda", "Toyota", "Nissan", "Ford"];
let meatsType: string[] = ["Fish", "Pork", "Beef", "Chicken"];
Enter fullscreen mode Exit fullscreen mode

Keep functions short and focused: One of the core principles of writing clean and maintainable code in JavaScript and TypeScript is keeping your functions concise and focused. To achieve this, break down larger functions into smaller, modular counterparts, each dedicated to a single, well-defined task. This approach enhances code modularity, making it easier to test and maintain.

Here's an example that demonstrates how to refactor a JavaScript function into TypeScript, following the "short and focused" principle:

Before Refactoring (JavaScript):

function calculateOrderTotal(order) {
  let total = 0;
  for (const item of order.items) {
    total += item.price * item.quantity;
  }

  if (order.promoCode) {
    total -= total * 0.1;
  }

  if (total > 1000) {
    total -= 100;
  }

  return total;
}
Enter fullscreen mode Exit fullscreen mode

After Refactoring (TypeScript):

interface Order {
  items: Array<{ price: number; quantity: number }>;
  promoCode?: string;
}

function calculateOrderTotal(order: Order): number {
  const subtotal = calculateSubtotal(order.items);
  const discountedTotal = applyPromoCode(subtotal, order.promoCode);
  return applyDiscountIfApplicable(discountedTotal);
}

function calculateSubtotal(items: Array<{ price: number; quantity: number }>): number {
  return items.reduce((acc, item) => acc + item.price * item.quantity, 0);
}

function applyPromoCode(subtotal: number, promoCode?: string): number {
  if (promoCode) {
    return subtotal - subtotal * 0.1;
  }
  return subtotal;
}

function applyDiscountIfApplicable(total: number): number {
  if (total > 1000) {
    return total - 100;
  }
  return total;
}
Enter fullscreen mode Exit fullscreen mode

Use comments in your code: Using comments to explain your code's intent is a crucial practice in maintaining clean and understandable code. Well-placed comments can provide clarity to both yourself and other developers who may work with your code in the future. They should describe the purpose of the code, its expected behavior, and any significant information that might not be evident from the code alone.

Here's an example in TypeScript illustrating the use of comments:

// Define a custom type for the array objects structure
type MyObject = {
  id: number;
  name: string;
};

/**
 * Sort an array of MyObject by their 'id' property in ascending order.
 *
 * @param array - The array to be sorted.
 * @returns A new array with objects sorted by 'id'.
 */
function sortObjectsById(array: MyObject[]): MyObject[] {
  return array.slice().sort((a, b) => a.id - b.id);
}

const arrDevelopers = [
  { id: 3, name: "Jorgel", language: "PHP" },
  { id: 0, name: "Eleazar", language: "Python" },
  { id: 1, name: "Hector", language: "Java" },
  { id: 2, name: "Alvison", language: "Golang" },
  { id: 4, name: "Bill", language: "JavaScript" },
];

sortObjectsById(arrDevelopers)

Enter fullscreen mode Exit fullscreen mode

Use WhiteSpace in your code: Whitespace is an important aspect of writing clean and maintainable code in JavaScript and TypeScript. Properly utilizing line breaks, indentation, and blank lines enhances code readability and makes it more visually appealing. Group related lines of code together and separate them from other blocks of code with blank lines. Let's take a look at an example in TypeScript:

function calculateTotalPrice(items: Item[]): number {
    let totalPrice = 0;

    for (const item of items) {
        totalPrice += item.price * item.quantity;
    }

    return totalPrice;
}

class Item {
    constructor(public name: string, public price: number, public quantity: number) {}
}

const shoppingCart: Item[] = [
    new Item("Laptop", 1000, 2),
    new Item("Mouse", 30, 3),
    new Item("Keyboard", 50, 1),
];

const total = calculateTotalPrice(shoppingCart);

console.log(`Total price: $${total}`);
Enter fullscreen mode Exit fullscreen mode

Avoiding using nested conditional statements: Use early returns or guard clauses to improve code readability and reduce complexity. Instead of multiple nested if-else statements, create functions or methods that return early when conditions are met. This approach enhances code organization and simplifies understanding for developers.

Here's an example in TypeScript demonstrating the use of early returns:

function calculatePrice(quantity: number, price: number, discountApplied: boolean): number {
    if (quantity <= 0) {
        return 0; // Guard clause to handle invalid input
    }

    let total = quantity * price;

    if (discountApplied) {
        total *= 0.9; // Apply a 10% discount
    }

    return total;
}
Enter fullscreen mode Exit fullscreen mode

Avoid Global Variables: A key principle for clean and maintainable JavaScript and TypeScript code is reducing global variable usage. Relying too much on globals can result in naming conflicts, unintended side effects, and code complexity. To address these concerns, opt for closures or module patterns to encapsulate your code and avoid global namespace pollution.

Here's an example demonstrating how to avoid global variables by encapsulating functionality within a TypeScript module:

// Instead of global variables, encapsulate your code in a module
namespace MyModule {
  // Private variable accessible only within the module
  let counter = 0;

  // Public function to interact with the module
  export function incrementCounter(): void {
    counter++;
  }

  // Public function to retrieve the counter value
  export function getCounterValue(): number {
    return counter;
  }
}

// Usage of the module functions
MyModule.incrementCounter();
console.log(MyModule.getCounterValue()); // Output: 1
Enter fullscreen mode Exit fullscreen mode

Use const and let instead of var: When declaring variables in JavaScript or TypeScript, it's best practice to utilize const and let rather than var. This choice enhances code readability, enforces block scoping, and mitigates the risk of accidental variable reassignment. By using const for values that won't change and let for variables that need to be reassigned, you can create cleaner and more predictable code. Here's an example in TypeScript:

// Using const for constants
const pi = 3.14;
const appName = "MyApp";

// Using let for variables
let counter = 0;
let username = "JohnDoe";

// Attempting to reassign a constant will result in a compile-time error
// pi = 3.14159; // Error: Cannot assign to 'pi' because it is a constant.

// Reassigning a variable is perfectly valid
counter = 1;
username = "JaneDoe";
Enter fullscreen mode Exit fullscreen mode

Use Template Literals: When it comes to writing clean and maintainable code in JavaScript and TypeScript, one essential practice is to use template literals for string interpolation. Template literals provide a concise and readable way to embed variables and expressions within strings, eliminating the need for messy concatenation or complex string manipulation. They are enclosed within backticks and can contain placeholders for variables or expressions enclosed in "${}".

Here's an example in EcmaScript:

// Define first and last names
const firstName = "John";
const lastName = "Doe";

// Create the full name using a template literal
const fullName = `My name is ${firstName} ${lastName}.`;

// Log the full name to the console
console.log(fullName);

Enter fullscreen mode Exit fullscreen mode

Use Arrow Functions: When working with simple callback functions in JavaScript and TypeScript, it's best practice to utilize arrow functions. Arrow functions offer a more concise and readable syntax compared to traditional function expressions. They automatically bind to the surrounding context, which can help reduce code verbosity. Here's an example in TypeScript to illustrate this:

// Traditional function expression
const add = function (a: number, b: number): number {
  return a + b;
};

// Arrow function for the same task
const add = (a: number, b: number): number => a + b;
Enter fullscreen mode Exit fullscreen mode

Use Object Destructuring: In JavaScript and TypeScript, clean and maintainable code hinges on object destructuring. This technique streamlines property access, enhancing code readability and reducing redundancy, especially when dealing with nested objects. Instead of accessing properties individually, object destructuring extracts the necessary properties, promoting cleaner and more intuitive code.

Here's a TypeScript example to illustrate the concept:

// A sample user object
const user = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com',
  address: {
    street: '123 Main St',
    city: 'Anytown',
    zipCode: '12345',
  },
};

// Without destructuring
const userName = user.name;
const userCity = user.address.city;

// With destructuring
const { name, address: { city } } = user;

console.log(`User Name: ${name}`);
console.log(`User City: ${city}`);
Enter fullscreen mode Exit fullscreen mode

Function Names: When naming functions in JavaScript and TypeScript, use clear verb-noun combinations for function names to convey their purpose effectively, making code more understandable for developers. For example, if you have a function that calculates the total price of items in a shopping cart, a well-named function might be calculateTotalPrice:

function calculateTotalPrice(items: Item[]): number {
  // Logic to calculate the total price
}
Enter fullscreen mode Exit fullscreen mode

Bonus: Optimize your code further by utilizing EcmaScript mode and arrow functions. Here are some illustrative examples:

const paymentUpdatedMessage = 'Payment has been updated!';
function payment(){
console.log(paymentUpdatedMessage);
}


// Best approach with regular JS and ES6
function updatePayment(){
console.log(paymentUpdatedMessage);
}

// Best approach using EcmaScript
const updatePayment = () =>
console.log(paymentUpdatedMessage);
}

Enter fullscreen mode Exit fullscreen mode

In conclusion, clean and maintainable code is essential for proficient web development. This ultimate guide for JavaScript and TypeScript emphasizes best practices to keep your codebase efficient, scalable, and collaborative. By consistently applying principles like DRY, KISS, SOLID, and focusing on meaningful naming and documentation, you'll elevate your code's quality and save time, making you a valuable team member.

So, keep refining your coding skills, stay updated with industry trends, and continue the journey towards writing cleaner, more elegant code. Happy coding!

❤️ Enjoyed the article? Your feedback fuels more content.
💬 Share your thoughts in a comment.
🔖 No time to read now? Well, Bookmark for later.
🔗 If it helped, pass it on, dude!

Top comments (7)

Collapse
 
aloisseckar profile image
Alois Sečkár • Edited

I humbly disagree with the "Use comments" part. Good clean code should be self-descriptive through clear variable and function names.

"Multiply width and height to get the area" followed by "const area = width * height;" is an example of a painfully useless information. Such obvious statements only bloat the code with no additional value.

The purpose of comments is to point out and explain some more complex (and often counter-intuitive) business logic behind the code or when you are tampering with some rather stable code upon a new change request. Like "// there is an exception for this value, because xy" or "// in a ticket_number this unique case was added". But usually when you feel the urge to explain a purpose of some block of code, it is a sign it should be extracted into new function - with a name that will basically stand for the comment.

Collapse
 
alvisonhunter profile image
Alvison Hunter Arnuero | Front-End Web Developer

Thank you for taking the time to share your perspective on the use of comments in JavaScript code. Your point of view is valuable, and I appreciate the opportunity to engage in this discussion.

I completely understand your argument for clean and self-descriptive code through clear variable and function names. It's true that well-chosen names can eliminate the need for many comments. Code that's easy to read and understand without comments is certainly an ideal to strive for.

However, I believe that there can be situations where comments are still useful, even in well-named code. Your mention of comments for complex or counter-intuitive logic is spot on. Comments can also help maintainers or team members understand the "why" behind certain decisions, even if the "what" is clear from the code itself. This can be particularly valuable when someone new joins a project or when you revisit your code after a significant period of time.

The idea of refactoring complex code into well-named functions is a good practice, and I agree that it often leads to cleaner and more maintainable code. In such cases, the function name can serve as a form of documentation. However, there might be situations where a piece of code is so straightforward and context-specific that extracting it into a separate function may not be warranted.

In summary, while I respect your perspective on minimizing comments, I would argue that there's still a place for them in code, even alongside well-named variables and functions. Striking the right balance between comments and clean code can be a matter of personal preference and the specific context of a project. Ultimately, the key is to ensure that your code remains understandable and maintainable for all team members.

Thank you for sharing your viewpoint, @aloisseckar and I encourage further discussion on this topic to help us all improve our coding practices.

Collapse
 
pouniok profile image
pouniok

I disagree with your point of view, I think comments are not only for explaining complex logic : you don't want to read a book to know what it's about, you just read the summary on the back. That's for the same purpose people are writing comments : make it easier for others to understand, without digging into algorithmic reflexions.

In my opinion clean code MUST have comments, because you don't want all people working on a project to read a whole file of code, understand what it does, summarize it, and remember it for the next 10 years, that's a waste of time with no added value.

Collapse
 
aloisseckar profile image
Alois Sečkár

I admit you also have a point. I guess it is about finding the right ballance. The part of this article about comments now look a bit different, much more reasonable than when I posted my reply.

There literally was:

// Multiply width and height to get the area
const area = width * height;
Enter fullscreen mode Exit fullscreen mode

I have noticed this tend to happen, when people apply the "comment your code" rule blindly. And I believe this is not helpful at all.

However, describing the general idea behind some block of code on slightly bigger scope, might be a very good habbit, as you suggest.

Collapse
 
shifi profile image
Shifa Ur Rehman

I stand by this.

Collapse
 
rukecodes1 profile image
rukecodes

Thanks for sharing all this information respect!!!!

Collapse
 
alvisonhunter profile image
Alvison Hunter Arnuero | Front-End Web Developer

we are glad to be of assistance @rukecodes1