A style guide is a set of standards that outline how code should be written and organized.
As you read through these guides, you can get an idea for how code is written
at the respective companies.
For one main reason: Everyone writes code differently.
I may like to do something one way, and you may like to do it a different way.
That’s all fine and dandy as long as we each work on our code.
But what happens when you have 10, 100, or even 1,000 developers all working on the same codebase?
Things get messy very quickly. Style guides are created so new developers can get up to speed on a code base quickly, and
then write code that other developers can understand quickly and easily!
-
13.1 Always use
constorletto declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. eslint:no-undefprefer-const// bad superPower = new SuperPower(); // good const superPower = new SuperPower();
-
13.2 Use one
constorletdeclaration per variable. eslint:one-varjscs:disallowMultipleVarDeclWhy? It’s easier to add new variable declarations this way, and you never have to worry about swapping out a
;for a,or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.// bad const items = getItems(), goSportsTeam = true, dragonball = 'z'; // bad // (compare to above, and try to spot the mistake) const items = getItems(), goSportsTeam = true; dragonball = 'z'; // good const items = getItems(); const goSportsTeam = true; const dragonball = 'z';
-
13.3 Group all your
consts and then group all yourlets.Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.
// bad let i; const items = getItems(); let dragonball; const goSportsTeam = true; let len; // good const goSportsTeam = true; const items = getItems(); let dragonball; let i; let length;
-
13.4 Assign variables where you need them, but place them in a reasonable place.
Why?
letandconstare block scoped and not function scoped.// bad - unnecessary function call function checkName(hasName) { const name = getName(); if (hasName === 'test') { return false; } if (name === 'test') { this.setName(''); return false; } return name; } // good function checkName(hasName) { if (hasName === 'test') { return false; } const name = getName(); if (name === 'test') { this.setName(''); return false; } return name; }
-
13.5 Don’t chain variable assignments. eslint:
no-multi-assignWhy? Chaining variable assignments creates implicit global variables.
// bad (function example() { // JavaScript interprets this as // let a = ( b = ( c = 1 ) ); // The let keyword only applies to variable a; variables b and c become // global variables. let a = b = c = 1; }()); console.log(a); // throws ReferenceError console.log(b); // 1 console.log(c); // 1 // good (function example() { let a = 1; let b = a; let c = a; }()); console.log(a); // throws ReferenceError console.log(b); // throws ReferenceError console.log(c); // throws ReferenceError // the same applies for `const`
-
13.6 Avoid linebreaks before or after
=in an assignment. If your assignment violatesmax-len, surround the value in parens. eslintoperator-linebreak.Why? Linebreaks surrounding
=can obfuscate the value of an assignment.// bad const foo = 'superLongLongLongLongLongLongLongLongString'; // good const foo = ( superLongLongLongLongLongLongLongLongFunctionName() );
-
15.1 Use
===and!==over==and!=. eslint:eqeqeqconst a = null; const b = undefined; console.log(a == b); // true console.log(a === b); // false
-
15.2 Conditional statements such as the
ifstatement evaluate their expression using coercion with theToBooleanabstract method and always follow these simple rules:- Objects evaluate to true
- Undefined evaluates to false
- Null evaluates to false
- Booleans evaluate to the value of the boolean
- Numbers evaluate to false if +0, -0, or NaN, otherwise true
- Strings evaluate to false if an empty string
'', otherwise true
-
15.3 Use braces to create blocks in
caseanddefaultclauses that contain lexical declarations (e.g.let,const,function, andclass). eslint:no-case-declarationsWhy? Lexical declarations are visible in the entire
switchblock but only get initialized when assigned, which only happens when itscaseis reached. This causes problems when multiplecaseclauses attempt to define the same thing.// bad switch (foo) { case 1: let x = 1; break; case 2: const y = 2; break; case 3: function f() { // ... } break; default: class C {} } // good switch (foo) { case 1: { let x = 1; break; } case 2: { const y = 2; break; } case 3: { function f() { // ... } break; } case 4: bar(); break; default: { class C {} } }
-
15.4 Ternaries should not be nested and generally be single line expressions. eslint:
no-nested-ternary// bad const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null; // split into 2 separated ternary expressions const maybeNull = value1 > value2 ? 'baz' : null; // better const foo = maybe1 > maybe2 ? 'bar' : maybeNull; // best const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
-
15.5 Avoid unneeded ternary statements. eslint:
no-unneeded-ternary// bad const foo = a ? a : b; const bar = c ? true : false; const baz = c ? false : true; // good const foo = a || b; const bar = !!c; const baz = !c;
-
15.6 When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators (
+,-,*, &/) since their precedence is broadly understood. eslint:no-mixed-operatorsWhy? This improves readability and clarifies the developer’s intention.
// bad const foo = a && b < 0 || c > 0 || d + 1 === 0; // bad const bar = a ** b - 5 % d; // bad // one may be confused into thinking (a || b) && c if (a || b && c) { return d; } // good const foo = (a && b < 0) || c > 0 || (d + 1 === 0); // good const bar = (a ** b) - (5 % d); // good if (a || (b && c)) { return d; } // good const bar = a + b / c * d;
-
18.1 Use
/** ... */for multi-line comments.// bad // make() returns a new element // based on the passed in tag name // // @param {String} tag // @return {Element} element function make(tag) { // ... return element; } // good /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; }
-
18.2 Use
//for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s on the first line of a block.// bad const active = true; // is current tab // good // is current tab const active = true; // bad function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; } // good function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this.type || 'no type'; return type; }
-
18.3 Start all comments with a space to make it easier to read. eslint:
spaced-comment// bad //is current tab const active = true; // good // is current tab const active = true; // bad /** *make() returns a new element *based on the passed-in tag name */ function make(tag) { // ... return element; } // good /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ... return element; }
-
18.4 Prefixing your comments with
FIXMEorTODOhelps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions areFIXME: -- need to figure this outorTODO: -- need to implement.class Calculator extends Abacus { constructor() { super(); // FIXME: shouldn’t use a global here total = 0; } }
class Calculator extends Abacus { constructor() { super(); // TODO: total should be configurable by an options param this.total = 0; } }
-
19.1 Use soft tabs (space character) set to 2 spaces. eslint:
indentjscs:validateIndentation// bad function foo() { ∙∙∙∙let name; } // bad function bar() { ∙let name; } // good function baz() { ∙∙let name; }
-
19.2 Place 1 space before the leading brace. eslint:
space-before-blocksjscs:requireSpaceBeforeBlockStatements// bad function test(){ console.log('test'); } // good function test() { console.log('test'); } // bad dog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog', }); // good dog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog', });
-
19.3 Set off operators with spaces. eslint:
space-infix-opsjscs:requireSpaceBeforeBinaryOperators,requireSpaceAfterBinaryOperators// bad const x=y+5; // good const x = y + 5;
-
19.4 End files with a single newline character. eslint:
eol-last// bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6;
// bad import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ↵
// good import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵
-
19.5 Leave a blank line after blocks and before the next statement. jscs:
requirePaddingNewLinesAfterBlocks// bad if (foo) { return bar; } return baz; // good if (foo) { return bar; } return baz;
-
19.6 Do not pad your blocks with blank lines. eslint:
padded-blocksjscs:disallowPaddingNewlinesInBlocks// bad function bar() { console.log(foo); } // bad if (baz) { console.log(qux); } else { console.log(foo); } // good function bar() { console.log(foo); } // good if (baz) { console.log(qux); } else { console.log(foo); }
-
19.7 Do not add spaces inside parentheses. eslint:
space-in-parensjscs:disallowSpacesInsideParentheses// bad function bar( foo ) { return foo; } // good function bar(foo) { return foo; } // bad if ( foo ) { console.log(foo); } // good if (foo) { console.log(foo); }
-
19.8 Do not add spaces inside brackets. eslint:
array-bracket-spacingjscs:disallowSpacesInsideArrayBrackets// bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // good const foo = [1, 2, 3]; console.log(foo[0]);
-
19.10 Add spaces inside curly braces. eslint:
object-curly-spacingjscs:requireSpacesInsideObjectBrackets// bad const foo = {clark: 'kent'}; // good const foo = { clark: 'kent' };
-
20.1 Leading commas: Nope. eslint:
comma-stylejscs:requireCommaBeforeLineBreak// bad const story = [ once , upon , aTime ]; // good const story = [ once, upon, aTime, ]; // bad const hero = { firstName: 'Ada' , lastName: 'Lovelace' , birthYear: 1815 , superPower: 'computers' }; // good const hero = { firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers', };
-
20.2 Additional trailing comma: Yup. eslint:
comma-danglejscs:requireTrailingCommaWhy? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the trailing comma problem in legacy browsers.
// bad - git diff without trailing comma const hero = { firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'] }; // good - git diff with trailing comma const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], };// bad const hero = { firstName: 'Dana', lastName: 'Scully' }; const heroes = [ 'Batman', 'Superman' ]; // good const hero = { firstName: 'Dana', lastName: 'Scully', }; const heroes = [ 'Batman', 'Superman', ]; // bad function createHero( firstName, lastName, inventorOf ) { // does nothing } // good function createHero( firstName, lastName, inventorOf, ) { // does nothing }
-
23.1 Avoid single letter names. Be descriptive with your naming. eslint:
id-length// bad function q() { // ... } // good function query() { // ... }
-
23.2 Use camelCase when naming objects, functions, and instances. eslint:
camelcasejscs:requireCamelCaseOrUpperCaseIdentifiers// bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {}
-
23.3 Use PascalCase only when naming constructors or classes. eslint:
new-capjscs:requireCapitalizedConstructors// bad function user(options) { this.name = options.name; } const bad = new user({ name: 'nope', }); // good class User { constructor(options) { this.name = options.name; } } const good = new User({ name: 'yup', });
-
23.4 Do not use trailing or leading underscores. eslint:
no-underscore-danglejscs:disallowDanglingUnderscoresWhy? JavaScript does not have the concept of privacy in terms of properties or methods. Although a leading underscore is a common convention to mean “private”, in fact, these properties are fully public, and as such, are part of your public API contract. This convention might lead developers to wrongly think that a change won’t count as breaking, or that tests aren’t needed. tl;dr: if you want something to be “private”, it must not be observably present.
// bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // good this.firstName = 'Panda'; // good, in environments where WeakMaps are available // see https://kangax.github.io/compat-table/es6/#test-WeakMap const firstNames = new WeakMap(); firstNames.set(this, 'Panda');
-
23.5 A base filename should exactly match the name of its default export.
// file 1 contents class CheckBox { // ... } export default CheckBox; // in some other file // bad import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename // bad import CheckBox from './check_box'; // PascalCase import/export, snake_case filename // good import CheckBox from './CheckBox'; // PascalCase export/import/filename
const MAX_FILE_SIZE = 1024;
-
24.1 Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use getVal() and setVal('hello').
// bad class Dragon { get age() { // ... } set age(value) { // ... } } // good class Dragon { getAge() { // ... } setAge(value) { // ... } }
-
24.2 If the property/method is a
boolean, useisVal()orhasVal().// bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; }