Skip to content

Instantly share code, notes, and snippets.

@qubyte
Created November 5, 2013 12:43
Show Gist options
  • Save qubyte/7318480 to your computer and use it in GitHub Desktop.
Save qubyte/7318480 to your computer and use it in GitHub Desktop.
Qubyte's JavaScript style guide

Style guide

Well written code by several authors should look like well written code by one author. To this end, everyone should write code in the same style and formatting. This serves multiple purposes, and it should be pretty clear what the benefits of consistent style with no arguments are.

This is the guide for the Entrago CMS backend codebase.

Basic formatting

Semicolons

Use them with statements. Note that blocks do not get semicolons, but do-while does after the condition. JSHint won't give you an option for this anyway.

Naming

All functions and variables should be named in camelCase. The two exceptions are constructor functions, which should be camelCase with the initial letter also in upper case, and constants, which should be in ALL_CAPS.

Whitespace

Variables assignment and operators.

A single space should separate variables from all operators. For example:

var a = 1;
var b = a * 2;

Functions

All functions, anonymous and named, get exactly one whitespace between function and the opening bracket for the argument list. For example:

var test = function (a, b, c) {
    ....
};

function test(a, b, c) {
    ....
}

// Maybe your function is recursive, so the following can make sense.
exports.recursive = function recursive(a, b, c) {
    ....
};

Note the semicolon in the first and third examples, which are expressions, whereas the second is a declaration.

Patterns

Avoid array methods and underscore

Any code in controllers should be considered hot. This means that any unnecessary array methods and underscore should be avoided for performance reasons in these. In fact, basically the only place an Array.prototype.forEach and family is appropriate is in startup code, since these code paths typically run once. The exception to this rule is if a function provided by underscore of the array prototype saves significant space and is easier to read. For the most part, a plain old for loop is the same size and just as readable.

Keep functions small

Keep functions small. If the function is more than 50 lines of code (with comments) then it probably needs to be broken up.

else is usually not necessary a.k.a return early and often

Given that functions are small, deep nesting of branched code is unlikely. This means that often you can simply return from if branches and avoid else altogether. This avoids and linearises nasty lumps of nested code. Your functions will have the appearance of filtering out the simple cases first.

It's ok to use the async module

If you have a set of nested callbacks, or find yourself counting callbacks, stop doing what you're doing and refactor your code to use the async module. In particular, get used to the async series, parallel, auto and each functions.

Don't use async.waterfall. async.waterfall makes it hard to move component functions around, and is a common source of bugs. Instead of waterfall, use series with subroutine like component functions editing data in the surrounding scope, to which all components have access. That brings us to...

Functions, pure functions, and Subroutines

Functions return a result and may also edit data in scopes that they have access to, or any arguments that are objects. Lower level languages sometimes make the distinction between functions and pure functions. Pure functions do not have any side effects, and may only return a result. i.e. they do not alter the arguments passed in or scopes outside their own. Going way back, fortran made the distinction between functions and subroutines. Subroutines are used to modify the arguments that are passed in, and so may be considered the opposite of a pure function.

Of course, in JavaScript there are no such limitations, but it can be useful to impose them. Have you ever seen an Array.prototype.map with other data being edited in the callback function? That is bad. If you want to do that, then use forEach and push. The map method should only take a pure function as a callback since that is the expectation of anyone coming from a functional programming background.

In the case of asynchronous subroutine-like functions, these may return a scope for chaining. Otherwise the rules apply to the arguments passed to the callback. There should only be one (optional) error argument. This fits will with the async methods series, parallel and auto.

Try wherever possible to make your functions, synchronous and asynchronous, either pure or subroutine-like. I promise that your code will become cleaner and easier to reason about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment