Coding Style
Code runs on machines, but it’s read by people. You’ll spend far more hours reading code than writing it, and most of that reading is your own code weeks later, or a teammate’s code you’ve never seen. Style is what makes that reading fast instead of painful.
The real skill in programming is taking something complicated and expressing it so that it’s both correct and obvious to a human. Clean style won’t fix a broken algorithm, but it removes a whole layer of friction between the reader and your intent.
Syntax
Before we argue each rule, here’s the whole cheat sheet on one small function. Everything below is just this figure, explained.
Here’s that same function as plain code, so you can copy it and see the shape:
function repeatText(text, count) {
let result = "";
for (let i = 0; i < count; i++) {
result += text;
}
return result;
}
let text = prompt("text?", "");
let count = prompt("count?", "");
if (count < 0) {
alert(`Count ${count} is not supported,
please enter a non-negative integer number`);
} else {
alert( repeatText(text, count) );
}
Now the rules, one at a time, with the reasoning behind each.
Curly Braces
Most JavaScript code puts the opening brace on the same line as the keyword, with a single space in front of it. This is often called “Egyptian” or K&R style:
if (condition) {
// do this
// ...and that
// ...and that
}
The interesting case is the one-liner: if (condition) doSomething(). Braces or no braces? Here are the common variants, ranked by how well they read:
- 😠 Braces crammed onto the same line as the body. This shows up in beginner code. It’s noisy and the braces earn nothing here:
if (count < 0) {alert(`Count ${count} is not supported`);} - 😠 Split across two lines with no braces. Avoid this one specifically. It looks fine until someone adds a second statement, and then the bug is invisible (more on that just below):
if (count < 0) alert(`Count ${count} is not supported`); - 😏 One line, no braces. Acceptable when the whole thing is genuinely short:
if (count < 0) alert(`Count ${count} is not supported`); - 😃 The safest default. A real block, braces included:
if (count < 0) { alert(`Count ${count} is not supported`); }
For something tiny like if (cond) return null, a single line is fine. In most other cases the full block reads more clearly and resists the trap in variant 2.
That trap is worth seeing concretely. Without braces, only the next single statement belongs to the if. Add a line meaning to extend the branch, and it silently runs unconditionally:
Line Length
Long horizontal lines are tiring to read and force sideways scrolling. Break them up.
Strings can be split using backtick (template) literals, which allow real line breaks inside them:
// backtick quotes ` allow the string to span multiple lines
let str = `
This welcome note stretches across several lines inside one
template literal, so the sentence stays comfortable to read
without forcing anyone to scroll sideways to reach the end.
`;
Long conditions read better broken onto several lines, with the operators trailing each line so the eye can see the logic stacking up:
if (
orderId === 512 &&
weekday === 'Thursday' &&
tier === 'Premium'
) {
unlockBonus();
}
Where to draw the limit is a team decision. Common caps are 80 or 120 characters. Pick one, put it in the linter, and stop debating it per line.
Indents
Two kinds of indentation matter, and they do different jobs.
Horizontal indent — 2 or 4 spaces. This is the nesting depth on the left edge. You can use 2 spaces, 4 spaces, or a real tab character (the Tab key). Spaces vs tabs is an ancient argument; spaces are more common today. One practical edge spaces have is fine-grained alignment — you can line arguments up under the opening bracket exactly:
show(parameters,
aligned, // 5 spaces of padding on the left
one,
after,
another
) {
// ...
}
Vertical indent — blank lines that group related code. Even one function usually contains a few distinct steps. A blank line between them tells the reader “new thought starts here.” In repeatText, the setup, the loop, and the result are three such steps:
function repeatText(text, count) {
let result = "";
for (let i = 0; i < count; i++) {
result += text;
}
return result;
}
Add a blank line wherever it clarifies structure. A loose rule of thumb: if a stretch runs past roughly nine lines with no break, it probably wants one.
Semicolons
End every statement with a semicolon, even when you could technically leave it off.
Some languages make semicolons truly optional and almost nobody types them. JavaScript is not quite that language. It has Automatic Semicolon Insertion, which usually guesses where statements end, but there are cases where a line break is not treated as a statement end, and the code misbehaves in ways that are hard to spot. The chapter on Code structure walks through those cases.
Experienced developers sometimes adopt a deliberate no-semicolon style such as StandardJS, which leans on ASI on purpose and has its own rules to stay safe. Until you know exactly where ASI bites, keep the semicolons. Most codebases do.
Nesting Levels
Deeply nested code is hard to follow — each level is another condition you have to hold in your head. Keep the nesting shallow where you can.
Inside a loop, the continue directive is a handy way to skip an iteration instead of wrapping the rest of the body in an if. Compare:
for (let i = 0; i < 10; i++) {
if (cond) {
... // <- one more nesting level
}
}
Flipping the condition and bailing early keeps the main work at the top level:
for (let i = 0; i < 10; i++) {
if (!cond) continue;
... // <- no extra nesting level
}
The same trick works with if/else and an early return. These two functions are equivalent:
// Option 1 — main work buried inside else
function repeatText(text, count) {
if (count < 0) {
alert("Negative 'count' not supported");
} else {
let result = "";
for (let i = 0; i < count; i++) {
result += text;
}
return result;
}
}
// Option 2 — handle the odd case first, then leave
function repeatText(text, count) {
if (count < 0) {
alert("Negative 'count' not supported");
return;
}
let result = "";
for (let i = 0; i < count; i++) {
result += text;
}
return result;
}
Option 2 reads better. The special case (count < 0) is dealt with and dismissed at the top — a “guard clause” — and everything after it is the normal path, un-indented and easy to scan.
Function Placement
Say you have a handful of helper functions plus the code that calls them. There are three ways to lay them out on the page.
-
Helpers first, then the code that uses them:
// function declarations function createElement() { ... } function setHandler(elem) { ... } function walkAround() { ... } // the code that uses them let elem = createElement(); setHandler(elem); walkAround(); -
Code first, helpers below:
// the code that uses the functions let elem = createElement(); setHandler(elem); walkAround(); // --- helper functions --- function createElement() { ... } function setHandler(elem) { ... } function walkAround() { ... } -
Mixed — each function declared right where it’s first needed.
The second layout is usually the nicest to read.
When you open a file, the first question is “what does this do?” Put the high-level code up top and the answer is right there. If the helper names are descriptive, you may never need to scroll down to their bodies at all. This works because function declarations are hoisted — the whole function is available before the line where it’s written, so calling createElement() above its definition runs fine.
Style Guides
A style guide is a document that answers the small recurring questions: single or double quotes, indent width, maximum line length, where blank lines go, and dozens of similar details. Individually they’re trivial. Collectively they decide whether a codebase looks like one voice or ten.
When everyone on a team follows the same guide, you can’t tell who wrote which file — and that’s the point. The code reads uniformly, so reviewers spend their attention on logic instead of formatting.
You can write your own guide, but you rarely need to. Well-maintained ones already exist:
- Google JavaScript Style Guide
- Airbnb JavaScript Style Guide
- Idiomatic.JS
- StandardJS
- (and plenty more)
If you’re just starting out, the cheat sheet at the top of this chapter is enough. Once you’re comfortable, skim a couple of the guides above to pick up ideas and settle on what you like.
Automated Linters
A linter reads your code and flags style violations for you — and often fixes them on save. You don’t have to remember the rules; the tool remembers them.
The bonus is that linters catch a class of real bugs too, not just formatting: a mistyped variable name, an unused import, an accidental global, a variable used before assignment. That safety net alone makes a linter worth running even if you’re indifferent to style.
Common tools:
- JSLint — one of the original linters, opinionated and minimal.
- JSHint — a more configurable descendant of JSLint.
- ESLint — the de facto standard today, highly configurable via plugins and shareable configs.
Any of them will do the job; ESLint is what most projects reach for now. Every mainstream editor has an integration — enable the plugin and it underlines problems as you type.
A typical setup with ESLint:
- Install Node.js.
- Install ESLint with a package manager, e.g.
npm install --save-dev eslint(npm ships with Node.js). - Add a configuration file at the root of your project.
- Install the ESLint plugin for your editor. Almost all of them have one.
Here’s a config in the older .eslintrc (JSON) format, which you’ll still see in many projects:
{
"extends": "eslint:recommended",
"env": {
"browser": true,
"node": true,
"es6": true
},
"rules": {
"no-console": 0,
"indent": 2
}
}
"extends" pulls in a base set of rules — here the built-in eslint:recommended — and then "rules" overrides individual ones on top (0 turns a rule off; 2 sets it to error). You can also extend published rule sets from the community instead of, or on top of, the defaults.
Some IDEs also ship built-in linting. It’s convenient, but usually less configurable than a dedicated ESLint setup you control.
Summary
Every rule here — and every rule in the guides linked above — exists to serve two goals: make the code easier to read, and make mistakes harder to write. That’s the whole test.
Whenever a style question comes up, don’t ask which side “wins.” Ask: does this make the code clearer to the next person, and does it help avoid errors? Those two questions resolve almost every debate. Skimming a popular style guide now and then keeps you current with how the wider community answers them.