Skip to content
  • Best Practices & Clean Code · JavaScript

Best Practices & Clean Code

LearnViaNotes @learnvianotes· 2 min read

Habits that keep JavaScript codebases readable, safe and fast — the things code reviewers look for.

Language habits

Readable code

JavaScript
// ✗ nested, magic numbers, unclear names
function f(u) {
  if (u) {
    if (u.a > 17) {
      if (u.s === 1) return true;
    }
  }
  return false;
}

// ✓ guard clauses, named constants, descriptive names
const ADULT_AGE = 18;
const STATUS = Object.freeze({ ACTIVE: 1, BANNED: 2 });
function canPurchase(user) {
  if (!user) return false;
  if (user.age < ADULT_AGE) return false;
  return user.status === STATUS.ACTIVE;
}
console.log(f({ a: 20, s: 1 }), canPurchase({ age: 20, status: STATUS.ACTIVE }), canPurchase(null));
Output
true true false

Security

RiskPractice
XSSUse textContent, not innerHTML, for user data; sanitise HTML (DOMPurify); set a Content-Security-Policy
eval / new Function / string setTimeoutNever run strings built from input
Prototype pollutionDon't deep-merge untrusted JSON into objects; block __proto__/constructor keys; use Map or Object.create(null)
SecretsNothing secret in client code or localStorage; HttpOnly cookies for sessions
Dependenciesnpm audit, lockfiles, minimal packages, pin and review updates
ReDoSAvoid nested quantifiers like (a+)+ on user input
JavaScript
const payload = JSON.parse('{"__proto__": {"isAdmin": true}}');
function naiveMerge(target, src) {
  for (const k in src) {
    if (typeof src[k] === "object") naiveMerge((target[k] ??= {}), src[k]);
    else target[k] = src[k];
  }
  return target;
}
naiveMerge({}, payload);
console.log("every object is now admin:", ({}).isAdmin);
delete Object.prototype.isAdmin;                          // undo the damage
console.log("after cleanup:", ({}).isAdmin);
Output
every object is now admin: true
after cleanup: undefined

Performance

  • Measure first (DevTools Performance panel, performance.now(), Lighthouse) — don't guess.

  • Pick the right structure: Set/Map lookups are O(1); array.includes inside a loop is O(n²).

  • Avoid blocking the main thread: chunk long work, use Web Workers, debounce/throttle event handlers.

  • Keep object shapes stable (initialise all fields in the constructor) so the JIT can optimise.

  • Ship less JavaScript: code-split with dynamic import(), tree-shake, lazy-load.

Tooling

ToolRole
ESLintCatches bugs and enforces conventions
PrettierFormatting — end style debates
TypeScript / JSDoc typesStatic types: catch whole classes of bugs before running
Vitest / Jest / node:testUnit tests
Vite / esbuild / webpackBundling, dev server, transpiling
JavaScript
import { test } from "node:test";
import assert from "node:assert/strict";

const slugify = (s) => s.trim().toLowerCase().replace(/\s+/g, "-");
assert.equal(slugify("  Hello World "), "hello-world");
assert.deepEqual([1, 2].map((x) => x * 2), [2, 4]);
assert.throws(() => JSON.parse("{"), SyntaxError);
console.log("all assertions passed");
Output
all assertions passed

How would you improve the performance of a slow page?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report