Journal

How to Write Cleaner Code with a JavaScript Refactor

Published by Aisha A. on Last modified Delivery & Quality

Cleaner code makes behavior easier to understand and changes easier to review. It does not mean the fewest lines, the most abstractions or adherence to one universal naming rule. Agree on the language and project conventions, then improve code where ambiguity creates maintenance risk.

This guide uses JavaScript and a small, dependency-free Node.js example. Its refactoring process transfers to other languages; its syntax and style choices do not.

Start by stating the behavior

Suppose a function totals active order lines. Each input has a non-negative integer price in cents, a non-negative integer quantity and a boolean active flag. Inputs are already validated, and the total stays within JavaScript’s safe integer range. Inactive lines contribute nothing. Taxes, discounts, currency conversion and payment handling are outside this example.

Here is a working but harder-to-read version:

function totalBefore(xs) {
  let n = 0;
  for (let i = 0; i < xs.length; i++) {
    if (xs[i].active === true) {
      n = n + xs[i].priceCents * xs[i].quantity;
    }
  }
  return n;
}

The behavior is small enough to describe, but the names hide units and purpose. Before changing it, capture the expected behavior with tests.

What Does Clean Code Mean

Make a focused refactor

This version exposes the collection, units and selection rule while keeping the same calculation:

function totalActiveOrderCents(orderLines) {
  let totalCents = 0;

  for (const line of orderLines) {
    if (line.active !== true) continue;
    totalCents += line.priceCents * line.quantity;
  }

  return totalCents;
}

The loop remains a loop. A chain of filter, map and reduce could also work, but brevity alone would not make it clearer. A separate multiplication helper would add indirection without clarifying much here. Extract a function when a meaningful concept or independent reason to change emerges.

16 Guidelines For Writing Clean Code

Run the behavioral checks

Copy both functions and the checks below into clean-code.cjs, then run node clean-code.cjs with Node.js installed. Successful execution prints the final message; a failed assertion stops the program.

const assert = require('node:assert/strict');

const cases = [
  { lines: [], expected: 0 },
  { lines: [{ active: false, priceCents: 500, quantity: 2 }], expected: 0 },
  { lines: [{ active: true, priceCents: 500, quantity: 0 }], expected: 0 },
  {
    lines: [
      { active: true, priceCents: 125, quantity: 2 },
      { active: false, priceCents: 999, quantity: 1 },
      { active: true, priceCents: 50, quantity: 3 }
    ],
    expected: 400
  }
];

for (const { lines, expected } of cases) {
  const snapshot = JSON.stringify(lines);
  assert.equal(totalBefore(lines), expected);
  assert.equal(totalActiveOrderCents(lines), expected);
  assert.equal(JSON.stringify(lines), snapshot);
}

console.log('All clean-code examples passed');

These checks cover empty, inactive, zero-quantity and mixed inputs, plus input mutation. They do not validate arbitrary external data. Add validation and its own tests at the system boundary if callers can supply malformed inputs. Integer cents simplify this example; real currency systems need explicit rules for supported currencies, rounding and numeric limits.

Use names that expose purpose and units

Prefer timeoutMilliseconds to timeout when confusion about units is possible. Prefer a domain name such as orderLines to a generic data when the narrower name is accurate. Do not mechanically delete words like Info or Manager if they communicate a real distinction.

Use the project’s conventions for casing. getUser, GetUser and get_user belong to different conventions; none is the mandatory spelling across languages. A function with several parameters may be clear. Use a structured options object when it groups a coherent concept or prevents ambiguous positional arguments, not simply to meet a three-parameter quota.

629713756ab211194aa164b3 629712be46afc85d9270b08e

Let tools settle formatting

Configure the repository’s formatter and linter, and use them consistently. This example uses semicolons. JavaScript Standard Style normally omits statement-ending semicolons and documents the exceptions needed for safe parsing. Do not combine contradictory style prescriptions and call them a language requirement.

Formatting cannot establish correctness. Review authorization, error handling, state changes and business rules separately. The guide to reducing software bugs covers broader quality practices.

Keep abstractions and comments useful

Group code around related responsibilities and dependencies. A small module should make its inputs, outputs and side effects apparent. Avoid splitting a coherent operation into many helpers that force readers to jump between files.

Repeated text is a prompt to investigate shared meaning. Two similar blocks may represent different business rules that will diverge later. Extract shared logic when they should change together; tolerate limited duplication while that relationship is uncertain.

Comments should explain a non-obvious constraint or decision, such as why a retry is disabled. Remove obsolete comments when behavior changes. Public interfaces may also need examples, error contracts and setup documentation even when implementation names are clear.

12- Avoid Writing Unnecessary Comments

Review the change as a maintenance decision

Ask a reviewer to explain the new behavior and identify the boundaries. Check whether names, tests and documentation agree. Keep a refactor separate from new behavior where practical so failures are easier to diagnose. For performance-sensitive code, measure representative workloads instead of assuming cleaner-looking code is faster.

A code smell is a signal to inspect, not proof of a defect. Tangled control flow, hidden state and unclear dependencies can make code hard to change, but a rewrite is not always justified. Improve the risky area in small steps, supported by relevant tests and a release path.

If maintenance problems are blocking delivery, Hapy can help scope an engineering review around the affected workflow and its risks.

Further questions

What are the advantages of clean code?

Clean code is vital because it helps you to communicate effectively with the subsequent person who will be working with your code. It’s critical, particularly in the software development environment, to be capable of referring to formerly written code and comprehending what it does.

How does clean code appear?

Clean code has the following characteristics: Readable code makes purpose, inputs, outputs and side effects clear. Useful names, consistent formatting and behavior-focused tests help; there is no universal visual test for cleanliness.

What does “code smells” mean?

A code smell is any trait in a system or a network that may signal a deeper issue in computer programming. The definition of a code smell is arbitrary and varies depending on the language, developer, and development approach.

What is the meaning of the term “spaghetti code”?

Spaghetti code describes tangled control flow and dependencies that make behavior difficult to follow or change. It is an informal description, not a measurable quality standard.


Share with others

Continue reading

More from the journal