JavaScript Best Practices
JavaScript's flexibility is both its strength and the source of most of its footguns — the language rarely stops you from writing something that technically runs but behaves unpredictably. These practices catch the mistakes that most reliably cause real bugs.
Last updated 2026-09-08
Use === and !== instead of == and !=
Loose equality performs type coercion before comparing, producing surprising results (0 == '' is true, null == undefined is true) that rarely match actual intent. Strict equality compares both value and type, avoiding an entire category of subtle bugs.
if (value === 0) { ... }if (value == 0) { ... } // also true for '', false, and other coerced valuesPrefer const by default, let when reassignment is genuinely needed, avoid var entirely
var has function-scoping and hoisting behavior that causes real confusion, especially inside loops and closures. const and let are block-scoped and behave predictably — default to const, and only use let when a variable genuinely needs to be reassigned.
Always handle Promise rejections
An unhandled Promise rejection can fail silently in some environments or crash a Node process in others, and the error's actual cause is often lost by the time anyone notices. Every async operation should have explicit error handling — a .catch(), or a try/catch around an await.
Avoid mutating function arguments and shared objects
Modifying an object or array passed into a function changes it for whatever code holds another reference to it, producing bugs that are hard to trace because the mutation happens far from where the unexpected behavior shows up. Return a new value instead of mutating in place where practical.
Use optional chaining and nullish coalescing instead of manual guard chains
Deeply nested property access on data that might be partially missing (a common shape for API responses) is both safer and more readable with ?. and ?? than a chain of manual && checks.
const city = user?.address?.city ?? 'Unknown';const city = user && user.address && user.address.city ? user.address.city : 'Unknown';Avoid deeply nested callbacks — use async/await
Callback-based asynchronous code nested several levels deep ('callback hell') is hard to read and even harder to add proper error handling to correctly at every level. async/await, built on Promises, expresses the same logic in a flat, sequential, far more readable form.
Be deliberate about equality checks on objects and arrays
Two objects or arrays with identical contents are not === equal to each other in JavaScript — equality compares references, not structure. Comparing them with === will almost always be false even when they 'look the same', which surprises people coming from languages with value-based equality.
Common Mistakes to Avoid
- ⚠Using == instead of === and getting bitten by unexpected type coercion
- ⚠Declaring everything with var, running into hoisting or closure-related bugs in loops
- ⚠Firing off an async operation without a .catch() or surrounding try/catch
- ⚠Mutating an object or array that's passed by reference, causing bugs elsewhere in the codebase
- ⚠Comparing two arrays or objects with === expecting a 'contents match' result instead of a reference check
- ⚠Deeply nested callback chains instead of using async/await for sequential asynchronous logic
Frequently Asked Questions
Is var ever the right choice today?
Essentially never in new code — let and const cover every case var used to handle, with more predictable scoping. var mainly persists in older codebases predating ES6.
Why does 0.1 + 0.2 not equal 0.3 in JavaScript?
This is a floating-point precision limitation shared by nearly every programming language, not a JavaScript-specific bug — numbers are stored in binary floating-point, which can't represent most decimal fractions exactly. For precise decimal math (currency, for instance), work in integer cents or use a dedicated decimal library.
Should I always use strict mode?
Modern JavaScript modules (ES modules, anything using import/export) are strict mode by default automatically — you generally don't need to add 'use strict' manually in current code using modules.