Choose only a single naming convention to be used in the project. Always check for typos.
- Use constant case for constants.
- Use upper camel case for component names (React).
- Use camel case for variable and function's names.
- Use dash case for route name.
Formatting | Name |
---|---|
TWO_WORDS | Constant case |
twoWords | Camel case |
TwoWords | Upper Camel case |
two-words | Dash case |
Using var
provides variable hoisting which is error-prone. Using const
and let
provides block scope that allows us to write clean and less error-prone code.
Have you ever felt guilty of taking a shortcut to name your variables x , y , z & i or naming your functions func1(), temp(), etc. Did you have a hard time understanding that same piece of code when you came back to it a few weeks later? Did you have to supplement that piece of code with inline code commentary? Please don’t do it — there is a better approach — self-documenting code.
❌
let t
✅
let elapsedTimeInDays
❌
getUserInfo();
getClientData();
getCustomerRecord();
✅
getUser();
❌
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
address.match(cityZipCodeRegex)[1],
address.match(cityZipCodeRegex)[2]
);
✅
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
❌
const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(l => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// Wait, what is `l` for again?
dispatch(l);
});
✅
const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(location => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch(location);
});
❌
const Car = {
carMake: "Honda",
carModel: "Accord",
carColor: "Blue"
};
function paintCar(car, color) {
car.carColor = color;
}
✅
const Car = {
make: "Honda",
model: "Accord",
color: "Blue"
};
function paintCar(car, color) {
car.color = color;
}
Default parameters are often cleaner than short circuiting. Be aware that if you use them, your function will only provide default values for undefined arguments. Other "falsy" values such as '', "", false, null, 0, and NaN, will not be replaced by a default value.
❌
function createMicrobrewery(name) {
const breweryName = name || "Hipster Brew Co.";
// ...
}
✅
function createMicrobrewery(name = "Hipster Brew Co.") {
// ...
}
Avoid using magic numbers in your code. Opt for searchable, named constants. Do not use single-letter names for constants since they can appear in many places and therefore are not easily searchable.
❌
if ( speed > 50 ) {
// body of if statement
}
✅
let SPEED_LIMIT = 50
if ( speed > SPEED_LIMIT ) {
// body of if statement
}
ECMAScript2020 has new features like null coalescing operator (??) and optional chaining operator (?) to check null or undefined values.
❌
const result = (a !== null && a !== undefined) ? a : b;
alert(result)
const arr=[]
if(arr && arr.length>10){
console.log('>10')
}
✅
alert(a??b)
const arr=[]
if(arr?.length>10){
console.log('>10')
}
Function names should be verbs if the function changes the state of the program, and nouns if they're used to return a certain value.
Functions should be small, really small. They should rarely be 20 lines long. The longer a function gets, it is more likely it is to do multiple things and have side effects.
❌
const setup=(text,subText) => <AlertDialog text={text} subText={subText} />
✅
const createAlertDialog=(text,subText) => <AlertDialog text={text} subText={subText} />
Limiting the amount of function parameters is incredibly important because it makes testing your function easier.
One or two arguments is the ideal case, and three should be avoided if possible.
You can use an object if you are finding yourself needing a lot of arguments.
- When someone looks at the function signature, it's immediately clear what properties are being used.
- It can be used to simulate named parameters.
- Linters can warn you about unused properties, which would be impossible without destructuring.
❌
function createMenu(title, body, buttonText, cancellable) {
// ...
}
createMenu("Foo", "Bar", "Baz", true);
✅
function createMenu({ title, body, buttonText, cancellable }) {
// ...
}
createMenu({
title: "Foo",
body: "Bar",
buttonText: "Baz",
cancellable: true
});
This is by far the most important rule in software engineering. When functions do more than one thing, they are harder to compose, test, and reason about. When you can isolate a function to just one action, it can be refactored easily and your code will read much cleaner. If you take nothing else away from this guide other than this, you'll be ahead of many developers.
❌
function emailClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
✅
function emailActiveClients(clients) {
clients.filter(isActiveClient).forEach(email);
}
function isActiveClient(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
❌
function addToDate(date, month) {
// ...
}
const date = new Date();
// It's hard to tell from the function name what is added
addToDate(date, 1);
✅
function addMonthToDate(month, date) {
// ...
}
const date = new Date();
addMonthToDate(1, date);
When you have more than one level of abstraction your function is usually doing too much. Splitting up functions leads to reusability and easier testing.
❌
function parseBetterJSAlternative(code) {
const REGEXES = [
// ...
];
const statements = code.split(" ");
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
// ...
});
});
const ast = [];
tokens.forEach(token => {
// lex...
});
ast.forEach(node => {
// parse...
});
}
✅
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const syntaxTree = parse(tokens);
syntaxTree.forEach(node => {
// parse...
});
}
function tokenize(code) {
const REGEXES = [
// ...
];
const statements = code.split(" ");
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
tokens.push(/* ... */);
});
});
return tokens;
}
function parse(tokens) {
const syntaxTree = [];
tokens.forEach(token => {
syntaxTree.push(/* ... */);
});
return syntaxTree;
}
Do your absolute best to avoid duplicate code. Duplicate code is bad because it means that there's more than one place to alter something if you need to change some logic.
Removing duplicate code means creating an abstraction that can handle this set of different things with just one function/module/class.
Getting the abstraction right is critical. Bad abstractions can be worse than duplicate code, so be careful!
Don't repeat yourself! Do not create your own error prone utility functions. Use libraries like Lodash and Ramda.
❌
const menuConfig = {
title: null,
body: "Bar",
buttonText: null,
cancellable: true
};
function createMenu(config) {
config.title = config.title || "Foo";
config.body = config.body || "Bar";
config.buttonText = config.buttonText || "Baz";
config.cancellable =
config.cancellable !== undefined ? config.cancellable : true;
}
createMenu(menuConfig);
✅
const menuConfig = {
title: "Order",
// User did not include 'body' key
buttonText: "Send",
cancellable: true
};
function createMenu(config) {
let finalConfig = Object.assign(
{
title: "Foo",
body: "Bar",
buttonText: "Baz",
cancellable: true
},
config
);
return finalConfig
// config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig);
Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean.
❌
function createFile(name, temp) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
✅
function createFile(name) {
fs.create(name);
}
function createTempFile(name) {
createFile(`./temp/${name}`);
}
A side effect could be writing to a file, modifying some global variable.
❌
// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
let name = "Ryan McDermott";
function splitIntoFirstAndLastName() {
name = name.split(" ");
}
splitIntoFirstAndLastName();
console.log(name); // ['Ryan', 'McDermott'];
✅
function splitIntoFirstAndLastName(name) {
return name.split(" ");
}
const name = "Ryan McDermott";
const newName = splitIntoFirstAndLastName(name);
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
In JavaScript, some values are unchangeable (immutable) and some are changeable (mutable). Objects and arrays are two kinds of mutable values so it's important to handle them carefully when they're passed as parameters to a function.
Cloning big objects can be very expensive in terms of performance. Luckily, this isn't a big issue in practice because there are great libraries e.g Immutable.js that allow this kind of programming approach to be fast and not as memory intensive.
❌
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};
✅
const addItemToCart = (cart, item) => {
return [...cart, { item, date: Date.now() }];
};
Monkey patching makes the team to get into the big trouble. Very hard to debug the bugs that it makes.
❌
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
✅
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
JavaScript isn't a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages can be cleaner and easier to test. Favor this style of programming when you can.
❌
const programmerOutput = [
{
name: "Uncle Bobby",
linesOfCode: 500
},
{
name: "Suzie Q",
linesOfCode: 1500
},
{
name: "Jimmy Gosling",
linesOfCode: 150
},
{
name: "Gracie Hopper",
linesOfCode: 1000
}
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
totalOutput += programmerOutput[i].linesOfCode;
}
✅
const programmerOutput = [
{
name: "Uncle Bobby",
linesOfCode: 500
},
{
name: "Suzie Q",
linesOfCode: 1500
},
{
name: "Jimmy Gosling",
linesOfCode: 150
},
{
name: "Gracie Hopper",
linesOfCode: 1000
}
];
const totalOutput = programmerOutput.reduce(
(totalLines, output) => totalLines + output.linesOfCode,
0
);
❌
if (fsm.state === "fetching" && isEmpty(listNode)) {
// ...
}
✅
function shouldShowSpinner(fsm, listNode) {
return fsm.state === "fetching" && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
We can use the decorator in functionnal programming style.
❌
function isDOMNodePresent(node) {
// ...
}
if (!isDOMNodePresent(node)) {
// ...
}
✅
function isDOMNodePresent(node) {
// ...
}
const not=(func)=>params=>!func(params)
const isDOMNodeNotPresent=not(isDOMNodePresent)
if (isDOMNodeNotPresent(node)) {
// ...
}
Sometimes, you can avoid conditionals like if/else or switch by using objects and dynamic keys.
❌
function getColorHashCode(name){
switch (name){
case "Red":
return "#ff0000";
case "Green":
return "#00ff00";
case "Blue":
return "#0000ff";
}
}
getColorHashCode("Red")
✅
const colors={
Red:"#ff0000",
Green:"#00ff00",
Blue:"#0000ff"
}
const getColorHashCode=(name)=>colors[name]
getColorHashCode("Red")
If you need type checking, just move to TypeScript or use Flow. TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale. FLOW IS A STATIC TYPE CHECKER FOR JAVASCRIPT. Flow is a static type checker for javascript but not a language.
❌
function travelToTexas(vehicle) {
if (vehicle instanceof Bicycle) {
vehicle.pedal(this.currentLocation, new Location("texas"));
} else if (vehicle instanceof Car) {
vehicle.drive(this.currentLocation, new Location("texas"));
}
}
✅
function travelToTexas(vehicle) {
vehicle.move(this.currentLocation, new Location("texas"));
}
Modern browsers do a lot of optimization under-the-hood at runtime. A lot of times, if you are optimizing then you are just wasting your time.
❌
// On old browsers, each iteration with uncached `list.length` would be costly
// because of `list.length` recomputation. In modern browsers, this is optimized.
for (let i = 0, len = list.length; i < len; i++) {
// ...
}
✅
for (let i = 0; i < list.length; i++) {
// ...
}
Dead code is just as bad as duplicate code. There's no reason to keep it in your codebase. If it's not being called, get rid of it! It will still be safe in your version history if you still need it. It can cost time and money to read it and to write test cases for it.
❌
function oldRequestModule(url) {
// ...
}
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker("apples", req, "www.inventory-awesome.io");
✅
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker("apples", req, "www.inventory-awesome.io");
❌
import { get } from "request-promise";
import { writeFile } from "fs-extra";
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
.then(body => {
return writeFile("article.html", body);
})
.then(() => {
console.log("File written");
})
.catch(err => {
console.error(err);
});
✅
import { get } from "request-promise";
import { writeFile } from "fs-extra";
async function getCleanCodeArticle() {
try {
const body = await get(
"https://en.wikipedia.org/wiki/Robert_Cecil_Martin"
);
await writeFile("article.html", body);
console.log("File written");
} catch (err) {
console.error(err);
}
}
getCleanCodeArticle()
❌
try {
functionThatMightThrow();
} catch (error) {
// this empty can lead you to the hell
}
✅
try {
functionThatMightThrow();
} catch (error) {
console.error(error);
}
Good code mostly documents itself.
❌
function hashIt(data) {
// The hash
let hash = 0;
// Length of string
const length = data.length;
// Loop through every character in data
for (let i = 0; i < length; i++) {
// Get character code.
const char = data.charCodeAt(i);
// Make the hash
hash = (hash << 5) - hash + char;
// Convert to 32-bit integer
hash &= hash;
}
}
✅
function hashIt(data) {
let hash = 0;
const length = data.length;
for (let i = 0; i < length; i++) {
const char = data.charCodeAt(i);
hash = (hash << 5) - hash + char;
// Convert to 32-bit integer
hash &= hash;
}
}
Version control exists for a reason. Leave old code in your history.
❌
doStuff();
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();
✅
doStuff();