DEV Community

Cover image for JavaScript Best Practices
Kafeel Ahmad (kaf shekh)
Kafeel Ahmad (kaf shekh)

Posted on

JavaScript Best Practices

In this article I will take you through “The good habits” when coding JavaScript.

1 — Avoid using new Object()
In JavaScript, using new Object is a bit risky, while using primitives always better for several reasons. Let’s dig deeper into this.

General speaking
The "" for example, creates a string primitive as we all know, on the other hand… new String() creates a string object. As they are more complex and have methods, string object can bring unexpected behavior, precisely when it comes to comparisons and type coercion.

Simplicity
Primitives are simpler in usage and more straightforward, as their usage avoids unnecessary complexity, and the code become easy to read and maintain.

Performance
Primitives are more efficient in terms of memory and performance. While creating an object involves additional overhead.

Possible confusion
Since JavaScript treats objects and primitives differently, using new Object()can lead to confusing situations where you’re unintentionally dealing with an object instead of a primitive, which might take you to a nest of bugs.

In most cases, it’s better to use primitives instead.


// ❌ Avoid
const str = new String();
const num = new Number();
const bool = new Boolean();
const obj = new Object();
const arr = new Array();
const regEx = new RegExp();
const func = new Function();

// ✅ Use
const str = "JavaScript";
const num = 10;
const bool = true;
const obj = {};
const arr = [];
const regEx = /()/;
const func = function() {};
Enter fullscreen mode Exit fullscreen mode

2 — Avoid using let with arrays and objects
First of all, let’s be clear… Using let with arrays and objects is not inherently problematic at all. But there are some specific considerations that might lead you to avoid it in certain cases:

Reassignment Vs. Mutation
As we all know, let allows us to reassign the variable itself, which can lead to confusion or data loss. An object / array can be reassigned by accident with an entire new set of data (new object / new array).

Using const instead makes it safer and clear that the reference to the object / array won’t change, but you can still modify it’s content.

Immutability Intent
Using const, you signal to other developers you work with that the variable should not be reassigned, enhancing code readability and maintainability.

Scope
While let has a block scope, it can lead to unexpected behavior in loops or conditional statements. By using const, the variable remains in scope without the risk of unintentional reassignment.

Best practice
Many coding standards and best practices encourage using const for variables that don’t need reassignment, promoting cleaner and more predictable code.


// ❌ Avoid
let book = { title: "Inferno", author: "Dan Brown" };

// The book object will be overrode with string
book = "Hello world";
// ✅ Use
const book = { title: "Inferno", author: "Dan Brown" };

// The book object cannot be overrode
book = "Hello world";
Enter fullscreen mode Exit fullscreen mode

3 — Be careful with Automatic Type conversion
Also known as type coercion, in JavaScript occurs when the language automatically converts a value from one type to another. This can happen in various situations, especially during operations involving different data types:

let sum = "5" + 1; // "51" (string concatenation)
// In the code above, typeof sum is a string

let sub = "5" - 1; // 4 (string converted to number)
// In the code obove, typeof sub in a number
Another example can be helpful:

let lang = "JavaScript"; // typeof name is string
lang = 15; // changes typeof x to a number
Enter fullscreen mode Exit fullscreen mode

Beware of numbers, can be converted to string or NaN by accident. Therefor, consider implementing type testing before sensitive operations or consider using TypeScript for safe typing.

4 — Avoid using double equal comparison
== and === are comparison operators used to compare values, but they behave differently.

Abstract Equality
When using ==, JavaScript converts the values to a common type before making the comparison


console.log(5 == '5'); // true (number is converted to string)
console.log(null == undefined); // true (considered equal)
console.log(0 == false); // true (0 is converted to boolean as it's falsy value)
Strict Equality
With ===, the comparison checks both the value and the type. If types are different, it returns false

console.log(5 === '5'); // false (different types)
console.log(null === undefined); // false (different types)
console.log(0 === false); // false (different types)
Enter fullscreen mode Exit fullscreen mode

When to Use ?
Use === when you want to ensure both value and type are the same, which is generally a good practice to avoid unexpected results.

Use == if you specifically need to compare values without considering their types, but this can lead to bugs and usually discouraged.

In general, consider using=== for more predictable and clear comparisons.

Note: Same thing goes with !== and !=

5 — Use Object / Array destructuring
In JavaScript, using destructuring technique with Objects and Arrays gives you several benefits.

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. As MDN web docs says.

Conciseness
It allows you to extract multiple properties from an object or elements from an array in a single statement, reducing the amount of code you need to write.

const book = { name: 'The Lost Symbol', author: 'Dan Brown' };
const { name, price } = book; // concise extraction
Enter fullscreen mode Exit fullscreen mode

Clarity
Destructuring can make your code more readable by clearly showing which properties or elements you are working with.


const colors = ['red', 'green', 'blue'];
const [firstColor, secondColor] = colors; // clear intention
Enter fullscreen mode Exit fullscreen mode

Default Values
You can easily assign default values if the property or element doesn’t exist.

const { height = 180 } = person; // uses default value if height is undefined
Enter fullscreen mode Exit fullscreen mode

Nested Destructuring
You can destructure nested objects or arrays, which can simplify accessing deeply nested data.

const user = { profile: { name: 'Eren Yeager', age: 20 } };
const { profile: { name } } = user; // easy access to nested properties
Enter fullscreen mode Exit fullscreen mode

Function Parameters
It can be useful for function parameters, allowing you to unpack values directly.

function display({ name, age }) {
    console.log(`${name} is ${age} years old.`);
}
Enter fullscreen mode Exit fullscreen mode

Destructuring helps streamline your code, making it cleaner and easier to maintain.

6 — Default parameters
Default parameters is a good technic to adopt to keep your code clearer and easy to read.

Default function parameters allow named parameters to be initialized with default values if no value or undefined is passed. As MDN web docs says.

Single parameter

function greet(name = 'Guest') {
    console.log(`Hello, ${name}!`);
}

greet();         // Output: Hello, Guest!
greet('Chrollo');  // Output: Hello, Chrollo!
Enter fullscreen mode Exit fullscreen mode

Multiple Parameters
You can set defaults for multiple parameters.

function multiply(a, b = 1) {
    return a * b;
}

multiply(5);    // Output: 5
multiply(5, 2);  // Output: 10
Enter fullscreen mode Exit fullscreen mode

Beware of non given parameters when passing multiples. Avoid passing the possibly undefinedor possibly non-passed parameter as the first one, or before any other passed parameters.

If you suspect that a parameter value could be not given or could be passed as undefined, ensure to pass it as the last one, as well as for multiple non given parameters.

Using Expressions as Defaults
You can use expressions to calculate default values.

function add(x, y = x) {
    return x + y;
}

add(5);    // Output: 10 (5 + 5)
add(5, 2);  // Output: 7
Enter fullscreen mode Exit fullscreen mode

Rest Parameters with Defaults
You can combine default parameters with rest parameters.

function logMessages(message, ...additionalMessages = ['No additional messages']) {
    console.log(message);
    console.log(additionalMessages);
}

logMessages('Hello!'); // Outputs: Hello! and ['No additional messages']
Enter fullscreen mode Exit fullscreen mode

Benefits
Improved Readability: It’s clear what defaults are used if arguments are omitted.
Less Boilerplate: Reduces the need for checking and assigning default values inside the function body.
Enhanced Flexibility: Functions can handle a wider range of inputs more gracefully.
Default parameters are a powerful feature that enhances function usability and makes your code cleaner!

7 —Use default in your Switches
Ending your switch statements with a default case is a good practice in JavaScript. The default case acts as a fallback when none of the specified cases match the input:

switch (expression) {
    case value1:
        // Code to execute if expression === value1
        break;
    case value2:
        // Code to execute if expression === value2
        break;
    default:
        // Code to execute if none of the above cases match
}
Enter fullscreen mode Exit fullscreen mode

Catch-All
It provides a way to handle unexpected values, ensuring that your code doesn’t silently fail.

const fruit = 'banana';

switch (fruit) {
    case 'apple':
        console.log('This is an apple.');
        break;
    case 'orange':
        console.log('This is an orange.');
        break;
    default:
        console.log('Unknown fruit.'); // Catches all other cases
}
Enter fullscreen mode Exit fullscreen mode

Improved Readability
Including a default case makes it clear to other developers (or yourself) that you considered all possibilities.

Error Handling
It can be used for logging or throwing errors when unexpected values are encountered.

function getColorCode(color) {
    switch (color) {
        case 'red':
            return '#FF0000';
        case 'green':
            return '#00FF00';
        case 'blue':
            return '#0000FF';
        default:
            throw new Error('Invalid color'); // Error handling
    }
}
Enter fullscreen mode Exit fullscreen mode

Always include a default case if there's a possibility of receiving unexpected input.
Use the default case to provide useful feedback or logging, especially in debugging scenarios.
Consider using a default case to set a fallback value if applicable.
Adding a default case to your switch statements enhances code robustness and maintainability.

8 — Avoid using eval()
The eval() is a built-in JavaScript function that takes a string as an argument and evaluates it as JavaScript code. This means you can dynamically execute code that is generated at runtime.

const x = 10;
const code = 'x + 5';
const result = eval(code); // Evaluates to 15
console.log(result); // Output: 15
Enter fullscreen mode Exit fullscreen mode

Avoiding the use of eval() in JavaScript is widely recommended due to several important reasons.

Security Risks
Code Injection: eval() can execute arbitrary code, making your application vulnerable to code injection attacks. If user input is evaluated, an attacker could inject malicious code.

const userInput = 'alert("Hacked!");'; // Malicious input
eval(userInput); // Executes the alert, compromising security
Enter fullscreen mode Exit fullscreen mode

Performance Issues
Slow Execution: Code executed with eval() runs slower than regular code because it has to be interpreted at runtime, bypassing certain optimizations made by JavaScript engines.
Debugging Challenges
Harder to Debug: Using eval() makes debugging difficult. Errors thrown inside eval() can be hard to trace back to the original source.
Alternatives
Instead of eval(), consider these safer alternatives:

JSON Parsing: If you’re dealing with JSON data, use JSON.parse() instead of eval().
const jsonString = '{"name": "Alice"}';
const obj = JSON.parse(jsonString); // Safe way to convert JSON string to an object
Function Constructors: If you need to dynamically create functions, consider using the Function constructor instead.

// In this case you can use new Function() -- back to 1st advice
const dynamicFunc = new Function('x', 'return x * x;');
console.log(dynamicFunc(5)); // Output: 25
Using Objects or Maps: For dynamic behavior, use objects or Maps to store key-value pairs instead of evaluating strings.
const operations = {
    add: (x, y) => x + y,
    subtract: (x, y) => x - y
};

const result = operations['add'](5, 3); // Safe and clear
Enter fullscreen mode Exit fullscreen mode

In summary, avoid using eval() due to security risks, performance issues, and debugging difficulties. Opt for safer alternatives to achieve your goals without compromising the integrity and performance of your code.

9 — Use Strict Mode
In JavaScript, “Strict Mode” is a way to opt into a restricted variant of the language, which helps catch common coding mistakes and “unsafe” actions. It can make your code more predictable and easier to debug.

Enable Strict Mode
Globally: By placing "use strict"; at the top of a script file.

"use strict";
// Your code here
Locally: By placing "use strict"; inside a function. This will enable Strict Mode only for that function.
function myFunction() {
    "use strict";
    // Strict mode is enabled here
}
Enter fullscreen mode Exit fullscreen mode

Benefits of Using Strict Mode
Prevents the use of undeclared variables: Assigning a value to an undeclared variable throws an error.

"use strict";
x = 3.14; // Throws ReferenceError: x is not defined
Enter fullscreen mode Exit fullscreen mode

Eliminates this coercion: In Strict Mode, this is undefined in functions that are called without an explicit context.

"use strict";
function myFunction() {
    console.log(this); // undefined
}
myFunction();
Disallows duplicate property names or parameters
"use strict";
const obj = {
    prop: 1,
    prop: 2 // Throws SyntaxError: Duplicate data property in object literal
};
Enter fullscreen mode Exit fullscreen mode

Prohibits certain syntax: Some syntax that is considered problematic or confusing is not allowed.
Common Pitfalls
Arrow functions: Note that arrow functions do not have their own this, so Strict Mode doesn't apply in the same way.
eval: Code executed within an eval statement runs in the local scope rather than the global scope.
Using Strict Mode is generally considered a best practice, especially for larger applications, as it helps you write cleaner and safer code.

10 — Keep Code DRY (Don’t Repeat Yourself)
The DRY (Don’t Repeat Yourself) principle is a key concept in software development aimed at reducing repetition in code. By ensuring that every piece of knowledge or logic is represented in a single place, you make your code easier to maintain, understand, and refactor.

Functions
Encapsulate repetitive logic in functions. This way, you can reuse the same code without duplication.

function calculateArea(width, height) {
    return width * height;
}

// Use the function instead of repeating the code
const area1 = calculateArea(5, 10);
const area2 = calculateArea(7, 3);
Enter fullscreen mode Exit fullscreen mode

Modules
Use modules to organize your code. This helps keep related functions and variables together, making them reusable across different parts of your application.

// math.js
export function add(a, b) {
    return a + b;
}

// main.js
import { add } from './math.js';
const sum = add(5, 10);
Enter fullscreen mode Exit fullscreen mode

Classes and Objects
Utilize classes or objects to group related data and behaviors. This encapsulation helps avoid repetition when working with similar data structures.

class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    area() {
        return this.width * this.height;
    }
}

const rect1 = new Rectangle(5, 10);
const rect2 = new Rectangle(7, 3);
Enter fullscreen mode Exit fullscreen mode

Note: If you’re adopting the “Functional Programming” paradigm in your daily coding, consider using any other tip but this one “Classes and Objects”.

Templates and Components
In web development, use templates or components (in frameworks like React, Vue, etc.) to encapsulate UI logic and styles that are reused.

// React component
function Button({ label, onClick }) {
    return <button onClick={onClick}>{label}</button>;
}

// Usage
<React.Fragment>
  <Button label="Click Me" onClick={handleClick} />
  <Button label="Submit" onClick={handleSubmit} />
</React.Fragment>
Enter fullscreen mode Exit fullscreen mode

Data Structures
Use arrays or objects to store related data rather than creating separate variables for each piece of data.

const members = [
    { name: 'Feitan Portor', nen: "Transmutation" },
    { name: 'Nobonaga Hazama', nen: "Enhancement"},
];

// Iterate over users without repeating code
members.forEach(member => console.log(member.name));
Configuration Objects
Use configuration objects to pass parameters to functions or classes instead of having multiple parameters.

function addMember({ name, nen }) {
    return { name, nen };
}

const member = addMember({ name: 'Illumi Zoldyck', nen: "Manipulation" });
Enter fullscreen mode Exit fullscreen mode

Applying the DRY principle leads to cleaner, more maintainable code. It helps minimize the risk of bugs since changes need to be made in only one place, and it enhances readability by reducing clutter. Remember that while it’s important to avoid repetition, there’s a balance to strike; over-abstracting can lead to complexity, so use your judgment when applying these principles.

11 — Use Meaningful Variable and Function Names
Using meaningful variable and function names is crucial for writing clear, maintainable, and understandable code.

Be Descriptive
Choose names that clearly describe the purpose or value of the variable or function.


// ❌ Bad
let x = 10; // What does x represent?

// ✅ Good

let itemCount = 10; // Clearly indicates it's a count of items.
Enter fullscreen mode Exit fullscreen mode

Use Action Words for Functions
Start functions with a verb that describes the action being performed.


// ❌ Bad
function process() {
    // Function details
}

// ✅ Good
function calculateTotalPrice() {
    // Function details
}
Enter fullscreen mode Exit fullscreen mode

Avoid Abbreviations
While short names might seem convenient, they can lead to confusion. Avoid abbreviations unless they are widely understood.


// ❌ Bad
let amt = 50; // What does amt mean?

// ✅ Good
let amount = 50; // Clear and understandable.
Enter fullscreen mode Exit fullscreen mode

Use Consistent Naming Conventions
Stick to a consistent naming convention throughout your codebase, such as camelCase for variables and functions, and PascalCase for classes.


function fetchData() {} // Function
const userList = []; // Variable
class UserProfile {} // Class
Enter fullscreen mode Exit fullscreen mode

Indicate Data Type or Purpose in Names
If a variable holds a specific type of data or serves a particular purpose, include that in the name.


// ❌ Bad
let data = []; // What kind of data?

// ✅ Good
let userProfiles = []; // Indicates it's an array of user profiles.
Enter fullscreen mode Exit fullscreen mode

Use Contextual Information
Consider the context in which the variable or function will be used to make names more meaningful.


// ❌ Bad
let list = []; // Vague

// ✅ Good
let todoList = []; // Clearly indicates it's for to-do items.
Enter fullscreen mode Exit fullscreen mode

Keep It Concise but Clear
While names should be descriptive, they shouldn’t be excessively long. Aim for a balance between clarity and brevity.


// ❌ Bad
let numberOfUsersInTheSystem = 100; // Too long

// ✅ Good
let userCount = 100; // Clear and concise.
Enter fullscreen mode Exit fullscreen mode

Use Domain-Specific Language
If you’re working in a specific domain (like finance, healthcare, etc.), use terms that are familiar to that domain.

let interestRate = 5.5; // Clear in a financial context.


Enter fullscreen mode Exit fullscreen mode

Refactor When Necessary
If you find that a name is no longer suitable as the code evolves, don’t hesitate to refactor it for better clarity.

let temp = 30; // After some time, this name may become unclear.
let roomTemperature = 30; // After refactor... More descriptive.
Enter fullscreen mode Exit fullscreen mode

Meaningful variable and function names significantly enhance code readability and maintainability. They help others (and yourself) understand the purpose and function of your code at a glance, making collaboration and debugging much easier. Always strive for clarity in your naming conventions.

12 — Avoid Global Variables
Avoiding global variables is a key practice in JavaScript (and programming in general) to maintain clean, modular, and maintainable code. Global variables can lead to unexpected behavior, naming conflicts, and difficulty in debugging.

Use Function Scope
Declare variables within functions to limit their scope and prevent them from being accessible globally.

function calculateTotal(items) {
    let total = 0; // Local scope
    items.forEach(item => total += item.price);
    return total;
}
Enter fullscreen mode Exit fullscreen mode

Use Block Scope with let and const
Utilize let and const to declare variables within blocks (like loops or conditionals), ensuring they are not accessible outside that block.

for (let i = 0; i < 10; i++) {
    let square = i * i; // `square` is block-scoped
    console.log(square);
}
// console.log(square); // ReferenceError: square is not defined
Enter fullscreen mode Exit fullscreen mode

Modularize Your Code
Organize your code into modules. Use ES6 modules or IIFE (Immediately Invoked Function Expressions) to encapsulate variables.

Using ES6 Modules:
// mathUtils.js
export function add(a, b) {
    return a + b;
}

// main.js
import { add } from './mathUtils.js';
const result = add(5, 10);
Using IIFE:
(function() {
    let privateVar = "I am private";
    function privateFunction() {
        console.log(privateVar);
    }
    privateFunction();
})();
Enter fullscreen mode Exit fullscreen mode

Encapsulate in Objects:
Group related variables and functions within an object to avoid polluting the global scope.

const calculator = {
    total: 0,
    add(num) {
        this.total += num;
    },
    reset() {
        this.total = 0;
    }
};
Enter fullscreen mode Exit fullscreen mode

Use Local Storage Wisely
If you need to persist data, consider using local storage, session storage, or indexedDB instead of global variables.

localStorage.setItem('userSettings', JSON.stringify(settings));
const settings = JSON.parse(localStorage.getItem('userSettings'));
Enter fullscreen mode Exit fullscreen mode

Limit the Use of Globals
If you must use global variables, limit their use to configuration constants or application-wide settings. Name them clearly to indicate their global nature.

const APP_VERSION = "1.0.0"; // A constant that might be needed globally

Enter fullscreen mode Exit fullscreen mode

Avoid Side Effects
When designing functions, avoid modifying global variables. This keeps functions predictable and easier to test.

let counter = 0;

function incrementCounter() {
    return ++counter; // Avoid this! Instead, return a new value.
}
Enter fullscreen mode Exit fullscreen mode

Use ‘this' Wisely
In object-oriented programming, use this to manage state within instances instead of relying on global variables.

class Counter {
    constructor() {
        this.count = 0;
    }

    increment() {
        this.count++;
    }
}
Enter fullscreen mode Exit fullscreen mode

By avoiding global variables, you enhance the modularity and maintainability of your code. It helps prevent naming conflicts and unintended side effects, making your code more predictable and easier to work with. Following these best practices will lead to cleaner and more manageable codebases.

13 — Use Promises and Async/Await for Asynchronous Code
Using Promises and async/await in JavaScript helps manage asynchronous operations more effectively, making your code cleaner and easier to read.

Understanding Promises
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value.

You can create a Promise using the Promise constructor:

const myPromise = new Promise((resolve, reject) => {
    // Simulate an asynchronous operation
    setTimeout(() => {
        const success = true; // Change to false to simulate failure
        if (success) {
            resolve('Operation successful!');
        } else {
            reject('Operation failed!');
        }
    }, 1000);
});
Enter fullscreen mode Exit fullscreen mode

Consuming a Promise
You can handle the outcome of a Promise using .then() for success and .catch() for error handling.

myPromise
  .then(result => {
      console.log(result); // 'Operation successful!'
  })
  .catch(error => {
      console.error(error); // Handle error
});
Enter fullscreen mode Exit fullscreen mode

Chaining Promises
You can chain multiple asynchronous operations using Promises.

fetch('https://jsonplaceholder.typicode.com/users')
  .then(response => response.json())
  .then(users => {
      console.log(users);
  })
  .catch(error => {
      console.error('Error:', error);
});
Enter fullscreen mode Exit fullscreen mode

Using Async/Await
async/await provides a more synchronous way to write asynchronous code, making it easier to read and maintain.

Declaring an Async Function:
Use the async keyword before a function to define it as an asynchronous function.

async function fetchUsers() {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users');
        const users = await response.json();
        console.log(users);
    } catch (error) {
        console.error('Error:', error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Calling Async Functions
You can call an async function just like a regular function. But, beware it will always return a Promise.

fetchUsers().then(() => {
    console.log('Data fetched successfully!');
});
Enter fullscreen mode Exit fullscreen mode

Handling Multiple Asynchronous Operations
You can use Promise.all to run multiple promises in parallel and wait for all of them to resolve.

async function fetchUsersPosts() {
    try {
        const [users, posts] = await Promise.all([
            fetch('https://jsonplaceholder.typicode.com/users').then(res => res.json()),
            fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json()),
        ]);
        console.log(users, posts);
    } catch (error) {
        console.error('Error:', error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Error Handling
Both Promises and async/await provide ways to handle errors gracefully.

Using .catch() with Promises:


myPromise
  .then(result => {
      console.log(result);
  })
  .catch(error => {
      console.error('Caught error:', error);
});
Enter fullscreen mode Exit fullscreen mode

Using try/catch with Async/Await:

async function example() {
    try {
        const result = await myPromise;
        console.log(result);
    } catch (error) {
        console.error('Caught error:', error);
    }
}
Enter fullscreen mode Exit fullscreen mode

Using Promises and async/await makes handling asynchronous operations in JavaScript much more manageable. They help avoid callback hell and improve code readability. Embracing these patterns will lead to cleaner, more maintainable, and error-resistant code.

14 — Document Your Code
Documenting your code is essential for maintaining clarity, aiding collaboration, and ensuring long-term maintainability.

Use Clear Comments
Explain “Why,” Not “What”: Focus on explaining why you did something rather than what the code does. The code itself should be readable enough to convey what it does.

// ❌ Bad: This doesn't provide much context
let x = 10; // Assigns 10 to x

// ✅ Good: Explains the purpose
let maxRetries = 10; // Maximum number of attempts for the API call
Enter fullscreen mode Exit fullscreen mode

Comment Complex Logic: For complex or non-obvious sections of code, provide detailed explanations.

// Check if user has the necessary permissions to access the resource
if (user.role === 'admin' || user.permissions.includes('access_resource')) {
    // Proceed with the operation
}
Enter fullscreen mode Exit fullscreen mode

Use Docstring Style Comments
In JavaScript, especially when using JSDoc, you can document functions, classes, and methods using structured comments.


/**
 * Calculates the total price including tax.
 * @param {number} price - The original price of the item.
 * @param {number} tax - The tax rate as a decimal.
 * @returns {number} The total price after tax.
 */
function calculateTotal(price, tax) {
    return price + (price * tax);
}
Document Public APIs
For libraries or modules, provide clear documentation for public APIs, including parameters, return values, and usage examples.

/**
 * Fetches user data from the server.
 * @async
 * @param {string} userId - The ID of the user to fetch.
 * @returns {Promise<Object>} A promise that resolves to the user data.
 * @throws {Error} Throws an error if the fetch fails.
 */
async function fetchUserData(userId) {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
        throw new Error('Failed to fetch user data');
    }
    return response.json();
}
Enter fullscreen mode Exit fullscreen mode

Maintain a README File
For projects, maintain a README.md file that provides an overview, installation instructions, usage examples, and contribution guidelines.

Effective documentation makes your code more understandable and maintainable, helping both current and future developers (including yourself) work efficiently. By incorporating these practices into your development workflow, you’ll foster better collaboration and reduce the learning curve for anyone interacting with your code.

Top comments (0)