-
Notifications
You must be signed in to change notification settings - Fork 5
JavaScript coding guidelines
adidas JavaScript coding guidelines are based on the Google JavaScript Style Guide with minor customization.
List of main rules, customized ones and examples.
For readability purposes the general coding rules are compiled below.
Rule | Reason |
---|---|
Statements | |
Use semicolon | Clarity, detect end of statements |
One statement per line | Clarity, readability |
Max one blank line between statements | Readability, grouping |
Use 2 spaces for indent | Compatibility across systems |
Use +1 indent for continuation | Readability, consistency |
Do not indent chains | Grouping |
Max line length 120 characters | Try to keep the line line between 80 and 100 characters if possible |
Ensure empty line at the end | Compatibility across systems |
Blocks | |
Open brace in the same line of keyword (function, class, method, conditional structure, object declaration, etc.) | Style, consistency |
Close brace in new line after block | Style, consistency |
Do not pad blocks with blank lines | Optimize space, consistency |
Comma expressions (declarations, objects, arrays, destructuring) | |
Comma at the end of expression | Style, consistency |
No extra comma on last expression | Clarity, detect end of expressions |
Strings | |
Use single quote | Style, consistency |
Use template instead of concatenation | Style |
Rule | Value |
---|---|
Whitespace | |
Binary operators, assignment | Spaces around operator |
Unary operators | No space between operator and identifier |
Semicolon | Space or new line after; no space nor new line before |
Colon | Space or new line after; no space nor new line before |
Comma | Space or new line after; no space nor new line before |
Block, class, namespace, module curly braces | Space before opening; no new line before opening; new line before closing |
Import/export curly braces | Space after opening brace; space before closing brace |
Objects, arrays, destructuring, interpolate expression | No space after opening; no space before closing |
Function | No spaces before parameters in named signature; no space before arguments in call |
Arrow function | Spaces around =>
|
Condition | Space before opening |
Ternary | Spaces around question mark; spaces around colon |
Inline comment / commented code | Space after //
|
Doc comment | Space after * ; space after tag |
import { readDir } from 'fs';
let a = 0,
b, c;
/**
* List contents of a directory async.
*
* @param {string} path - path to read.
* @param {string} options.cwd - working directory.
* @returns {Promise<Array<string>, Error>} promise handler.
*/
function asyncReadDir(path, { cwd = null }) {
return new Promise((resolve, reject) => {
function handler(error, data) {
if (error) {
reject(error)
} else {
resolve(data);
}
}
readDir(path, { cwd }, handler);
});
}
/**
* List contents of a directory.
*
* @param {string} path - path to read.
* @param {?string} cwd - current working directory.
* @returns {Promise<Array<string>>} promise handler.
*/
export function listDir(path, cwd) {
return asyncReadDir(path, { cwd })
.then((data) => {
console.info(`Found ${ data.length } items`);
return data;
})
.catch((error) => {
console.warn(error);
// fail safe with empty array
return [];
});
}
All the descriptors and names in JavaScript are written using the next four formats. The regular expression is only showing regular characters, but numbers, and other special characters can also be used, as well as the _
leading a private member.
Format | Regular expression |
---|---|
camelCase | /^[a-z][a-zA-Z]*&/ |
PascalCase | /^[A-Z][a-zA-Z]*&/ |
kebab-case | /^[a-z](-?[a-z])*&/ |
UPPER_SNAKE_CASE | /^[A-Z_]*&/ |
Naming rule | Format |
---|---|
Files and directories in scope of JavaScript | kebab-case |
Classes | PascalCase |
Named functions, parameters, class members and object properties | camelCase |
Variables (leading underscore can be used, trailing numbers should be avoided in general) | camelCase |
Constants (booleans, strings and numbers) | UPPER_SNAKE_CASE |
Module names | camelCase |
Good practice | Reason |
---|---|
Use meaningful identifiers | Clarity, readability |
Avoid abbreviations | Clarity, readability |
Avoid single character identifiers in general (but indexes) | Clarity, readability |
Variable names have to be a noun or a group working as a noun | Understandability |
Array variables have to satisfy variable rules but in plural form | Understandability |
Boolean variable names have to be an adjective or verb in participle form or a group working as an adjective | Understandability |
Function names have to start with a verb | Understandability |
class Worker {
constructor(title, tasks) {
this.title = title;
this.parallelTasking = false;
this.tasks = tasks;
}
startTask() {
const MAX_TASKS_IN_QUEUE = 999;
const _tasks = this.tasks.map((task) => task.name);
let first = _tasks.pop();
// TODO
}
stopTask() {
// TODO
}
}
const tasks = [
{ name: 'task-a' },
{ name: 'task-b' }
];
const worker = new Workder('title', tasks);
worker.startTask();
Rule | Reason |
---|---|
Do not use constant conditions | Prevent infinite loops and dead branches |
Join nested if-else clauses as if-elseif-else | Unless there is a good reason, helps with readability |
End do-while with semicolon after condition | Use semicolon to terminate statements |
Indent case clause on same column of switch keyword |
Case clauses are labels, not blocks. (No block, no indentation) |
Ensure break after case | Prevent unexpected fallthrough cases |
Always add default case to switch cases |
Handling unexpected conditions might help to catch errors |
- Conditions (
if
/else
):// correct if (condition) { ... } if (condition) { ... } else { ... } // illegal if(condition){ ... } if ( condition ) { ... } if (condition) { ... } else { ... } if (condition) { ... } else { ... }
- Loop expressions (
while
/for
):// correct for (statements) { ... } while (statements) { ... } // illegal for(statements){ ... } for ( statements ) { ... } for (statements) { ... } while(statements){ ... } while ( statements ) { ... } while (statements) { ... }
- Loop expressions (
do while
):// correct do { ... } while (condition); // illegal do{ ... }while(condition); do { ... } while ( condition ); do { ... } while (condition);
- Switch expressions (
switch
):// correct switch (letter) { case 'a': ... break; default: ... } // illegal switch(letter){ case 'a': ... break; default: ... } switch ( letter ) { case 'a': ... break; default: ... } switch (letter) { case 'a': ... break; default: ... } switch (letter) { case 'a': ... break; } switch (letter) { case 'a': ... break; default: ... }
// some inline comment
Rule | Reason |
---|---|
Do not abuse inline comments | Too many comments might imply excessive complexity in the code, if possible, the code should be understandable by itself |
Do not abuse TODO, FIXME or FUTURE comments | Most of these will never be checked again |
// correct
function foo() {
// some comment
...
}
// illegal
function foo() {
/*
* some comment
*/
/* some comment */
...
}
/**
* Description.
* @param {ParamType} param - Parameter description.
* @returns {ParamType} Description.
*/
The JavaScript documentation is based on @use JSDoc format.
Rule | Reason |
---|---|
Set optional/defaults for parameters as required | Documentation, completion |
All parameters must be documented with type, name and description: use @param
|
Clarity |
Returns must be documented with type and description: use @returns
|
Clarity |
All documentation must have a description | Clarity |
Description should be separated from tags by one blank line | Readability |
Exported and internal functions must be documented, skip block scoped | Clarity, interoperability, completion |
// correct
/**
* Function description.
* @param {?number} total - The total value of elements.
* @returns {number} The average value.
*/
function someFunction(foo) {
...
}
class {
/**
* Attribute description.
* @type {string}
*/
someAttribute;
/**
* Method description.
* @param {number} [foo=3] - a value.
* @returns {number} a new value.
*/
public someMethod(foo) {
...
}
}
// return execute(parameter, 'always')
// .then((result) => {
// return get(result);
// });
Rule | Reason |
---|---|
Commented code is now allowed in repositories | Commented code is most definitely dead code, if not necessary, then remove it |
Note: if there is a important reason in order not to remove the commented code, it has to be added to the commented block with a TODO.
// TODO: activate when the the `always` type is ready.
// return execute(parameter, 'always')
// .then(processResult)
// .catch((error) => {
// // TODO: error management
// console.warn('Error: ', error);
// });
Rules applied to the new features added to the newest versions of JavaScript.
Note: remember that from ES6 onwards, they require transpilation to ES5 if the code has to run in a browser. For NodeJS JavaScript code it is not necessary.
Features that only apply to ES5.
var PI = 3.14;
var foo = new Foo();
Rule | Reason |
---|---|
Always declare variables with var | Non declared variables pollute the global window object |
Do not use single var declaration |
Clarity |
Initialize one variable per line | Readability |
A list of non-initialized variables can be grouped inline | Readability, optimize space |
// correct
function foo() {
var logger = new Logger();
var index = 0;
var variableName, anotherVariableName;
}
// illegal
function foo() {
var logger = new Logger(),
index = 0,
aVariable, anotherVariable;
}
const PI = 3.14;
const list = [];
let variableName = 'a variable which can be reassigned';
Rule | Reason |
---|---|
Use let and const instead of var
|
var causes hoisting which can be prevented with block scoped declarations |
Do not group let /const declarations; sort: const declarations, let declarations |
Clarity |
Initialize one variable per line | Readability |
A list of non-initialized variables can be grouped inline | Readability, optimize space |
No const without initialization |
Error |
// correct
function foo() {
const PI = 3.14;
const list = [];
let variableName = 'a variable which can be reassigned';
}
// illegal
function foo() {
const PI = 3.14,
list = [];
let variableName = 'a variable which can be reassigned', anotherVariable;
}
function foo(fn) {
...
}
foo(() => {
...
});
Rule of thumb:
- Named, prototype or exported:
function
.- Short way inside classes and objects.
- Callback: arrow function (
=>
).
Rule | Reason |
---|---|
Use function keyword for named, prototype or exported functions |
Easier to spot and reuse; this is accessible within prototype
|
Use arrows for callbacks | Contextual this and readability |
Functions should not have state | Pure functions are recommended as functional programming best practices |
Spread operator can be used as needed | Utility, prevent usage of arguments |
Use parenthesis around arrow parameters | Consistency |
Use immediate return, in arrow function, whenever possible, and wrap returned objects with parenthesis | Utility, readability, optimize space |
Use default parameters as needed | Utility |
Use optional parameters as needed | Utility |
- Functions:
// correct function foo() { ... } export function doSomething() { ... } export default function doMoreThings() { ... } // illegal: named arrows const foo = () => { ... }; export const doSomething = () => { ... }; const doMoreThings = () => { ... }; export default doMoreThings; // illegal: function assignment const foo = function() { ... }; export const doSomething = function() { ... }; const doMoreThings = function() { ... }; export default doMoreThings;
- Arrow functions:
// correct someAsyncMethod((parameter) => { // TODO }); someAsyncMethod(() => ({ some: 'object' })); someAsyncMethod(() => someStatement); someAsyncMethod((...args) => { console.log(args); // list of arguments }); someAsyncMethod(() => { this.prop = 'value'; // accessing parent context }); // illegal someAsyncMethod(() => { return { some: 'object' }; }); someAsyncMethod(() => { return someStatement; }); someAsyncMethod(function() { // TODO }); someAsyncMethod(function callback() { // TODO }); someAsyncMethod(event => { // TODO }); someAsyncMethod(() => { console.log(arguments); // `arguments` is not defined }); const _this = this; // preserve context through var assign someAsyncMethod(() => { _this.prop = 'value'; });
For further information about functions and arrow functions see this StackOverflow thread.
const person = { name: 'name' , age: 99, projects: [] };
const description = `The employee ${ person.name } is ${ person.age } years old`;
Rule | Reason |
---|---|
Use template string instead of concatenation | Style and performance |
// correct
const description = `The employee ${ person.name } works in ${ projects.join(', ') }`;
// illegal
const description = 'The employee ' + person.name + ' works in ' + projects.join(', ');
import { Item } from 'module';
export { Item }
Rule | Reason |
---|---|
Third party imports on top | Clarity |
Group imports from same module | Clarity |
Wildcard imports are allowed | Some modules must be used this way |
Avoid default export |
Default export is not consistent across systems and could cause trouble |
Sort of imports order is neither checked nor enforced, yet this is the preferred sorting:
- External module dependencies.
- Local parent dependencies.
- Local internal dependencies.
// correct
import { Component } from 'react';
import { find, get, map } from 'lodash';
import { calculateAge } from '../modules/people/Age';
import { Person } from './Person';
export { Person };
// illegal
import { Person } from './Person';
import { calculateAge } from '../modules/people/Age';
import * as React from 'react';
import { find } from 'lodash';
import { get } from 'lodash';
import { map } from 'lodash';
export { property: value };
class ClassName {
_field;
static get staticField() {
...
}
constructor() {
this.property = 'value';
}
doSomething() {
...
}
}
Rule | Reason |
---|---|
Prefix private members with _
|
Convention, readability, completion |
Declare a single class per file | Clarity, SRP |
Remove empty constructors | Optimize space |
Use static accessors for static fields | Convention |
Sort members by C# style guide:
- Members: fields > constructors > methods.
- Visibility within members: public (default) > private.
- Modifiers within visibility: static > instance (default).
class Foo {
someProp;
_anotherProp = false;
constructor(someProp) {
this._someProp = someProp || 'defaultValue';
}
static get staticProp() {
...
}
doSomething() {
...
}
doSomething(param) {
...
}
_doSomethingElse() {
...
}
}
New features:
-
Array.prototype.includes
. - Decorators.
- Exponentiation operator:
**
.
ES7 new features work well with existing rules hence no additional rules are required.
See Babel configuration in the package Babel preset ENV.
New features:
- Shared memory and atomics.
- String padding:
padStart
/padEnd
. - Object methods:
values
/entries
,getOwnPropertyDescriptors
. - Support trailing commas in function parameters, lists and calls.
- Asynchronous code with
async
/await
.
Most ES8 features work well with existing rules but:
- To keep consistency with previous rules, trailing commas are still discouraged.
See Babel configuration in the package Babel preset ENV.
See Babel preset React.
In order to check most of the style rules, there are available configurations for JavaScript and its different versions using ESLint linting tool.
All of the configurations are in the repository js-linter-configs, and they are available via NPM as separated modules:
- EcmaScript 5.1 Language Specification [Ecma International]
- EcmaScript 2015 Language Specification [Ecma International]
- EcmaScript 2016 Language Specification [Ecma International]
- EcmaScript 2017 Language Specification [Ecma International]
- JSX Homepage [JSX GitHub IO]
- Facebook JSX [Facebook GitHub IO]
- ESLint Homepage [ESLint]
- Google JS Style Guide for pre ES6 JavaScript [Google GitHub IO]
- Google JS Style Guide for ES6 and newer JavaScript [Google GitHub IO]
- AngularJS Developer Guide [Angular Docs]
- AngularJS John Papa styleguide [GitHub/johnpapa]
- JSDoc Homepage