How to Write Readable JavaScript Code: Naming, Branches, and Functions

This article explains why readable code matters for developers, then offers practical guidelines on naming variables and functions, structuring conditional branches, keeping functions single‑purpose, handling errors, avoiding side‑effects, and organizing class methods to improve maintainability in modern JavaScript projects.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
How to Write Readable JavaScript Code: Naming, Branches, and Functions

Writing readable code is crucial for programmers because the primary purpose of code is to be read, followed by correct execution. Unreadable but functional code increases maintenance cost, reduces stability, and harms team cohesion.

The author shares personal experiences on how to improve code readability, focusing on ES6/7 features that aid this goal.

JavaScript is dynamic and weakly typed, which made “quick and casual” coding acceptable in the IE6 era, but as front‑end projects grow, that habit becomes a major obstacle. This article discusses ES6/7 code because these versions have largely replaced classic JavaScript and provide features that improve readability.

Variable Naming

Variable names are the foundation of readable code. Names must convey enough information. For example, getData is vague, while fetchUserInfoAsync clearly indicates an asynchronous operation that returns a Promise.

Naming Basics

Usually nouns name objects and verbs name functions. Example:

monkey.eat(banana); // the monkey eats a banana
const apple = pick(tree); // pick an apple from the tree

Using plural nouns for collections (e.g., bananas for an array of banana) or suffixes like List or Map makes intent explicit.

Some words have identical singular and plural forms, and uncountable nouns like data or information lack a plural; in those cases a suffix such as List can be used to denote a collection.

Naming in Context

Names should fit the surrounding scope. In a zoo‑management example, a function feedAnimals processes different food items depending on the current context, so variables like banana or peach make the logic clear.

function feedAnimals(food, animals) {
  // ...
  const banana = bananas.pop();
  if (banana) {
    monkey.eat(banana);
  } else {
    const peach = peaches.pop();
    monkey.eat(peach);
  }
}

The function’s purpose is obvious: feed all animals with available food, and the variable names reflect that purpose.

Strict Naming Conventions

Adopting a consistent naming rule eliminates the need to remember multiple variants. For instance, always using fetchUserInformation instead of alternating with getUserData makes the code self‑explanatory.

Branch Structures

Well‑structured branches resemble a binary tree, making mental simulation easier. However, many developers write confusing nested or combined conditions.

Bad Practice: Return Inside a Branch

function foo() {
  if (condition) {
    // branch 1 logic
    return;
  }
  // branch 2 logic
}

Placing the main logic after an early return hides execution flow. Moving the secondary logic into an else block clarifies intent.

function foo() {
  if (condition) {
    // branch 1 logic
  } else {
    // branch 2 logic
  }
}
If a branch is intentionally empty, leave an empty line to signal that no action is required.

Bad Practice: Combining Multiple Conditions

if (cond1 && cond2 && cond3) {
  // main logic
} else {
  // secondary logic
}

When several conditions can fail independently, the combined form obscures which specific condition leads to the secondary path. Splitting them into nested if‑else statements makes each case explicit.

if (cond1) {
  if (cond2) {
    // main logic
  } else {
    // branch 2 logic
  }
} else {
  // branch 3 logic
}

Bad Practice: Using Branches to Mutate State

let foo = someValue;
if (condition) {
  foo = doSomethingToFoo(foo);
}
// continue using foo

Such patches accumulate and degrade readability. Prefer immutable patterns or explicit transformations, e.g., using a ternary expression.

const foo = condition ? doSomethingToFoo(someValue) : someValue;

Functions

One Function, One Responsibility

A function that handles both single‑user and multiple‑user requests ( fetchUserInfo) forces readers to infer hidden rules. Splitting it into fetchSingleUser and fetchMultipleUser clarifies intent and eases maintenance.

async function fetchMultipleUser(idList) {
  return await request.post('/api/users/', {idList});
}

async function fetchSingleUser(id) {
  return await fetchMultipleUser([id])[0];
}

Proper Error Handling

Silently swallowing errors (e.g., using || or &&) hides failures. Instead, propagate errors or handle them explicitly, allowing callers to decide how to react.

const result = await fetchUserFavorites(id);
if (result.success) {
  user.favoriteBooks = result.data.books;
} else {
  user.favoriteBooks = [];
}
// or simply let the exception bubble up
user.favoriteBooks = (await fetchUserFavorites(id)).books;

Controlling Side Effects

Pure functions avoid hidden side effects. A function that mutates a passed‑in object (e.g., addFavoritesToUser) makes the caller unaware of the changes. Returning a new object or a structured result keeps the contract clear.

async function getUserDetail(id) {
  const user = await fetchSingleUserInfo(id);
  const {books, songs, isMusicFan} = await getUserFavorites(id);
  return Object.assign(user, {books, songs, isMusicFan});
}

async function getUserFavorites(id) {
  const {books, songs} = await fetchUserFavorites(id);
  return {books, songs, isMusicFan: songs.length > 100};
}

Non‑Intrusive Refactoring

When adding caching to fetchUserInfo, wrapping the original function with a decorator ( memorizeThunk) preserves readability and isolates the new concern.

const memorizeThunk = (func, reducer) => {
  const cache = {};
  return (...args, callback) => {
    const key = reducer(...args);
    if (cache[key]) {
      callback(...cache[key]);
    } else {
      func(...args, (...result) => {
        cache[key] = result;
        callback(...result);
      });
    }
  };
};

const fetchUserInfoCache = memorizeThunk(fetchUserInfo, userId => userId);

Class Structure

Avoid Overusing Member Methods

Helper functions that are only used inside render (e.g., chunk for grouping users) should be defined locally rather than as class members, preventing unnecessary exposure and reducing cognitive load for readers.

Conclusion

Just as writers refine prose, developers should periodically review and refactor code for readability. By paying attention to naming, branch clarity, single‑purpose functions, error handling, side‑effect control, and thoughtful class design, we develop a “code sense” that leads to higher‑quality, maintainable software.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptbest practicesnaming conventionsfunctionscode readability
Taobao Frontend Technology
Written by

Taobao Frontend Technology

The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.