Modern JavaScript Best Practices
Published: 10 Sep, 2025
Written by

Sujeet Mishra
Director at Entelligo
Modern JavaScript Best Practices: Writing Cleaner, Safer and More Performant Code
JavaScript is the most widely used programming language in the world and has played a crucial role in shaping one of humanity’s most impactful technologies - the internet. With this influence comes responsibility. The JavaScript ecosystem evolves rapidly which makes it challenging to keep pace with the latest best practices.
In this article, we’ll explore modern JavaScript best practices that will help you write cleaner, more maintainable and more performant code.
Project Rules First
Before diving into global best practices, remember this golden rule: project-defined practices override all else.
Every project may have its own coding standards, guidelines, and conventions. These rules ensure consistency across the team, which is more valuable than following an external best-practices list. If you want to adopt a new practice, align it with your project rules and get team consensus first.
Core Best Practices in Modern JavaScript
1. Use let and const Instead of var
for (let j = 1; j < 5; j++) {
console.log(j);
}
console.log(j); // ReferenceError
Why: Unlike var (function-scoped), let and const are block-scoped, reducing unexpected bugs and improving predictability.
2. Prefer Classes Over Prototype Functions
Old way:
function Person(name) {
this.name = name;
}
Person.prototype.getName = function() {
return this.name;
}
Modern way:
class Person {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
Why: Classes provide cleaner, more readable syntax and true OOP semantics.
3. Use Private Class Fields
class Person {
#name;
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
}
Why: #privateFields ensure true encapsulation, unlike the old _underscore convention.
4. Adopt Arrow Functions for Conciseness
const numbers = [1, 2]; numbers.map(num => num * 2);
Why: Arrow functions simplify callbacks and automatically bind this, avoiding common pitfalls.
5. Use Nullish Coalescing (??) Instead of ||
const value = 0; const result = value ?? 10; // 0 (expected) Why: ?? respects valid falsy values (0, false, "") while only defaulting for null or undefined.
6. Simplify Access with Optional Chaining (?.)
const tax = product?.price?.tax;
Why: Eliminates repetitive null checks when dealing with nested objects.
7. Prefer async/await Over .then() Chains
async function fetchData() {
try {
const res = await fetch('/api');
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}
Why: Makes asynchronous code readable and manageable, with clean error handling.
8. Use Object.entries() and Object.values() for Iteration
Object.entries(obj).forEach(([k, v]) => console.log(k, v));
Why: Cleaner than manual for...in loops, especially with dynamic objects.
9. Check Arrays with Array.isArray()
console.log(Array.isArray([1,2,3])); // true
Why: Reliable across different contexts (unlike instanceof).
10. Use Map for Key-Value Pairs
const map = new Map(); map.set({ id: 1 }, 'value');
Why: Unlike objects, Maps allow any type of key (not just strings or symbols).
11. Use Symbol for Hidden or Unique Keys
const hiddenKey = Symbol('hidden'); obj[hiddenKey] = 'secret';
Why: Prevents property collisions and keeps internal data hidden.
12. Check Intl API Before Using Extra Libraries
const formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }); console.log(formatter.format(123456.78)); // $123,456.78
Why: The Intl API handles locale formatting natively, often eliminating the need for third-party libraries.
Common Practices That Should Be Best Practices
Always use strict equality (===)
: Avoids weird coercion bugs (0 == '' // true).
Be explicit in if conditions
: Don’t rely on implicit truthy/falsy checks for values like 0 or "".
Avoid floating-point errors with decimal.js or big.js
: Critical for financial calculations.
Handle big integers carefully
: Use BigInt, and customize JSON serialization with replacer/reviver.
Use JSDoc for clarity
: Helps IDEs and teammates understand expected inputs/outputs.
Write tests
: With Node.js (20+), Bun, or Deno, you get built-in test runners. Use Playwright for browser E2E testing.
Remember: Consistency and clarity trump cleverness. Use these practices as guidelines, but always align with your project rules.