DEV Community

Cover image for Clean Code in JavaScript: A Comprehensive Guide πŸš€
Alaa Samy
Alaa Samy

Posted on

Clean Code in JavaScript: A Comprehensive Guide πŸš€

Writing clean code is an essential skill for any developer. Clean code isn't just about making your code workβ€”it's about making it work elegantly, efficiently, and in a way that other developers (including your future self) can easily understand and maintain. In this comprehensive guide, we'll explore the principles and best practices of writing clean JavaScript code.

What is Clean Code?

Clean code is code that is:

  1. Readable: Easy to understand at a glance
  2. Maintainable: Simple to modify and debug
  3. Reusable: Can be repurposed for different scenarios
  4. Testable: Easy to write unit tests for
  5. Scalable: Can grow without becoming complex

1. Variables: The Building Blocks of Clean Code

- Use Meaningful Variable Names

Your variable names should clearly indicate their purpose and context.

// Bad
const d = new Date();
let u = getUser();
const arr = ['Apple', 'Banana', 'Orange'];

// Good
const currentDate = new Date();
let currentUser = getUser();
const fruitList = ['Apple', 'Banana', 'Orange'];
Enter fullscreen mode Exit fullscreen mode

- Use Constants for Fixed Values

When a value won't change, use const instead of let or var.

// Bad
var API_ENDPOINT = 'https://api.example.com';
var MAX_ITEMS = 100;

// Good
const API_ENDPOINT = 'https://api.example.com';
const MAX_ITEMS = 100;
Enter fullscreen mode Exit fullscreen mode

- Maintain Consistent Naming Convention

Use consistent naming patterns throughout your codebase.

// Bad - Inconsistent naming
getUserInfo()
getClientData()
getCustomerRecord()

// Good - Consistent naming
getUser()
updateUser()
deleteUser()
Enter fullscreen mode Exit fullscreen mode

- Use Searchable Names

Make your variables and constants easily searchable.

// Bad
setTimeout(doSomething, 86400000);

// Good
const MILLISECONDS_IN_A_DAY = 86400000;
setTimeout(doSomething, MILLISECONDS_IN_A_DAY);
Enter fullscreen mode Exit fullscreen mode

2. Objects: Organizing Data Cleanly

- Use Getters and Setters

Encapsulate object properties using getters and setters.

// Bad
class User {
  constructor(name) {
    this.name = name;
  }
}

// Good
class User {
  #name;  // Private field

  constructor(name) {
    this.#name = name;
  }

  getName() {
    return this.#name;
  }

  setName(name) {
    this.#name = name;
  }
}
Enter fullscreen mode Exit fullscreen mode

- Implement Private Members

Use private fields and methods to protect object data.

class BankAccount {
  #balance = 0;  // Private field

  deposit(amount) {
    if (this.#validateAmount(amount)) {
      this.#balance += amount;
    }
  }

  #validateAmount(amount) {  // Private method
    return amount > 0;
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Functions: The Heart of Clean Code

- Keep Functions Small and Focused

Each function should do exactly one thing.

// Bad
function processUserData(user) {
  validateUser(user);
  updateUserInDatabase(user);
  sendWelcomeEmail(user);
  updateUIWithUserData(user);
}

// Good
function processUserData(user) {
  if (validateUser(user)) {
    saveUserData(user);
  }
}

function saveUserData(user) {
  updateUserInDatabase(user)
    .then(sendWelcomeEmail)
    .then(updateUIWithUserData);
}
Enter fullscreen mode Exit fullscreen mode

- Limit Function Parameters

Use objects to pass multiple parameters.

// Bad
function createUser(firstName, lastName, email, age, location) {
  // ...
}

// Good
function createUser(userConfig) {
  const { firstName, lastName, email, age, location } = userConfig;
  // ...
}
Enter fullscreen mode Exit fullscreen mode

- Use Descriptive Function Names

Function names should clearly describe what they do.

// Bad
function proc(data) { /* ... */ }

// Good
function processUserPayment(paymentData) { /* ... */ }
Enter fullscreen mode Exit fullscreen mode

4. Comments: When and How to Use Them

- Write Self-Documenting Code

Your code should be clear enough that it doesn't need extensive comments.

// Bad
// Check if user is adult
if (user.age >= 18) { /* ... */ }

// Good
const isAdult = user.age >= 18;
if (isAdult) { /* ... */ }
Enter fullscreen mode Exit fullscreen mode

- Use Comments for Complex Logic

Comments should explain "why" not "what".

// Bad
// Iterate through users
users.forEach(user => { /* ... */ });

// Good
// Filter inactive users before sending notifications to avoid
// overwhelming users who haven't logged in for 30+ days
const activeUsers = users.filter(user => user.lastLogin > thirtyDaysAgo);
Enter fullscreen mode Exit fullscreen mode

5. Testing: Ensuring Code Quality

- Write Tests First (TDD)

Consider writing tests before implementing features.

// Example test
describe('User Authentication', () => {
  it('should successfully login with valid credentials', () => {
    const user = new User('test@example.com', 'password123');
    expect(user.login()).toBeTruthy();
  });
});
Enter fullscreen mode Exit fullscreen mode

- Test Edge Cases

Always test boundary conditions and error cases.

describe('Array Utility', () => {
  it('should handle empty arrays', () => {
    expect(processArray([])).toEqual([]);
  });

  it('should handle null input', () => {
    expect(() => processArray(null)).toThrow('Invalid input');
  });
});
Enter fullscreen mode Exit fullscreen mode

6. Modern JavaScript Features for Cleaner Code

- Use Optional Chaining

// Bad
const streetName = user && user.address && user.address.street;

// Good
const streetName = user?.address?.street;
Enter fullscreen mode Exit fullscreen mode

- Utilize Destructuring

// Bad
const firstName = user.firstName;
const lastName = user.lastName;

// Good
const { firstName, lastName } = user;
Enter fullscreen mode Exit fullscreen mode

- Implement Default Parameters

// Bad
function greet(name) {
  name = name || 'Guest';
  return `Hello, ${name}!`;
}

// Good
function greet(name = 'Guest') {
  return `Hello, ${name}!`;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Writing clean code is an ongoing journey of improvement. It's not just about following rulesβ€”it's about developing a mindset that values clarity, simplicity, and maintainability. Remember:

  • Write code for humans first, computers second
  • Keep your functions small and focused
  • Use meaningful names for variables and functions
  • Test thoroughly
  • Refactor regularly
  • Stay consistent with your coding style

By following these principles and continuously refining your approach, you'll write code that's not just functional, but truly professional and maintainable.


References

Top comments (12)

Collapse
 
lexlohr profile image
Alex Lohr

About #2 using classes to organize data: it is always a bad idea to conflate data with functionality - and wrapping everything in getters and setters will just inflate your code. Unless you need to take action on getting and setting, those are pure overhead - and otherwise, use actual getters and setters so the users of your objects don't need to inflate their code, too:

// Don't! 
class User {
  #name; 
  constructor(name) {
    this.#name = name; 
  }
  getName() {
    return this.#name; 
  }
  setName(name) {
    this.#name = name; 
  }
}
const user = new User(name);

// Just do
const user = { name };
Enter fullscreen mode Exit fullscreen mode

See how much complexity vanishes just because you handle data as data and not as class?

Collapse
 
pengeszikra profile image
Peter Vivo • Edited

You are right: Function is the heart of Clean Code.
But let's realize a main difference between the arrow function and the classic function.
The classic function have hoisting and can be used as class.
Arrow function is much modern, don't use inner this and for a good reason have a return value.

because in your example:

// Good
function processUserData(user) {
  if (validateUser(user)) {
    saveUserData(user);
  }
}

function saveUserData(user) {
  updateUserInDatabase(user)
    .then(sendWelcomeEmail)
    .then(updateUIWithUserData);
}
Enter fullscreen mode Exit fullscreen mode

All of these functions are inpure because use some outer dependency like saveUserData(user)

My advice is we need to make distinct ( at least in mind level ) between the pure and in pure function.

If we would like to make our function with outer dependency are testable then better to use dependency injection:

/** 
 * @type {( 
 *    updateUserInDatabase: (u:User) => void,
 *    sendWelcomeEmail:  (u:User) => void,
 *    updateUIWithUserData:  (u:User) => void,
 * ) => ( u:User ) => Promise<void> }
 */
const saveUserDataFactory = (
  updateUserInDatabase,
  sendWelcomeEmail,
  updateUIWithUserData,
) => async (user) => 
  updateUserInDatabase(user)
    .then(sendWelcomeEmail)
    .then(updateUIWithUserData)
  ;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
alaa-samy profile image
Alaa Samy

It's a really good approach! Thank you for sharing this 😊

Collapse
 
aayla_secura profile image
@AaylaSecura

Good article, thank you :) I didn't quite understand your example on naming conventions though. Can you explain why getUserInfo, getClientData and getCustomerRecord have inconsistent naming and are bad?

Collapse
 
alaa-samy profile image
Alaa Samy

I am glad you find this article useful 😊.

Let me explain the inconsistency in the naming functions (getUserInfo, getClientData, getCustomerRecord )
These functions use different terms (Info, Data, Record) and refer to essentially similar concepts (User, Client, Customer), and this might be confusing to someone who reads the code and might not recognize what these functions are related to.
Also, these naming variations make it hard for developers to remember and search in the code, and figure out which function to call.

I hope this clarifies things for you, Feel free to ask any further questions

Collapse
 
almeyda profile image
Gabriel Almeida

I think is because all of them have different names for the same functionality, get the user data (sorry for any mistakes, English isn't my native language)

Collapse
 
st3adyp1ck profile image
Ilya Belous

Wow, what an awesome deep dive into clean JavaScript code! πŸš€ This guide is a goldmine for anyone looking to level up their coding gameβ€”whether you're a newbie or a seasoned dev. I love how you broke it down into actionable tips like meaningful variable names (goodbye, cryptic d and arr!) and keeping functions small and focused. That β€œdo one thing” rule is a game-changerβ€”I’m guilty of writing those monster functions that try to do everything at once. πŸ˜…

The modern JS features section is πŸ”₯β€”optional chaining and destructuring are my go-tos for making code cleaner AND more readable. And that tip about writing comments for β€œwhy” instead of β€œwhat”? Spot on. It’s so tempting to over-comment the obvious stuff instead of explaining the real intent.

Quick question for the community: How do you all handle naming conventions in big projects? I’ve been leaning toward camelCase for consistency, but I’d love to hear your strategies! Also, any favorite tools or linters you swear by to enforce clean code? (ESLint fans, where you at?)

Thanks for dropping this knowledgeβ€”definitely bookmarking it for my next refactoring session.

Collapse
 
alaa-samy profile image
Alaa Samy

I'm thrilled to hear that you found the guide helpful 🌟,

When it comes to naming conventions in big projects, consistency is key.
Personally, I stick to camelCase for folder names, hooks, and utility files, and for components I use PascalCase, It helps keep everything organized and makes the codebase easier to navigate.

Thanks again for your kind words and for joining the discussion! πŸš€

Collapse
 
jakehadley profile image
JakeHadley

I really love the ai generated circle of interactions y'all got going on. It's almost like it's real life. Great ai article as well. Maybe I should do one on Rust clean code when I know nothing about Rust.

Thread Thread
 
alaa-samy profile image
Alaa Samy

Thanks for your comment!

Actually, I wrote this article based on my experience working with JavaScript and studying clean code principles, even though I did use AI tools to help organize and format some content (as many writers do with tools like Grammarly or spell checkers).

Contributing to the developer community is valuable, and the focus should be on the content quality and usefulness rather than how it was created.

I'd genuinely encourage you to write and share your knowledge! and I'd be happy to discuss any specific points about clean code practices in JavaScript if you have questions or insights to share.

Collapse
 
rixlabs profile image
Riccardo

Nice to see Clean code in the Javascript world. It would be good to acknowledge the masters of the past.

Collapse
 
learndepth_2024_63d7d74d5 profile image
learndepth 2024

alert("Hi")