Skip to content

Instantly share code, notes, and snippets.

@jameswyse
Last active August 29, 2015 14:14

Revisions

  1. Eran Hammer revised this gist Feb 4, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ Whether you use 2 spaces or 4 spaces, there are a few simple things that can mak

    ### Required modules

    JavaScript makes is harder than most languages to know where variables are coming from. Variables assigned required modules are particularly important because they represent a singleton object shared with the entire application. There are also globals and module globals, along with function variables and arguments.
    JavaScript makes it harder than most languages to know where variables are coming from. Variables assigned required modules are particularly important because they represent a singleton object shared with the entire application. There are also globals and module globals, along with function variables and arguments.

    Traditionally, variables starting with an uppercase letter represent a class that must be instantiated using `new`. This was an important semantic in the early days of JavaScript but at this point, if you don't know `Date` requires `new Date()` you are probably very new. We have adopted Upper Camel Case variable names for all module global variables which are assigned required modules:

  2. Eran Hammer revised this gist Feb 4, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ Whether you use 2 spaces or 4 spaces, there are a few simple things that can mak

    JavaScript makes is harder than most languages to know where variables are coming from. Variables assigned required modules are particularly important because they represent a singleton object shared with the entire application. There are also globals and module globals, along with function variables and arguments.

    Traditionally, variables starting with an uppercase letter represent a class that must be instantiated using `new`. This was an important semantic in the early days of JavaScript but at this point, if you don't know `Date` requires `new Date()` you are probably very new. We have adopted CamelCase variable names for all module global variables which are assigned required modules:
    Traditionally, variables starting with an uppercase letter represent a class that must be instantiated using `new`. This was an important semantic in the early days of JavaScript but at this point, if you don't know `Date` requires `new Date()` you are probably very new. We have adopted Upper Camel Case variable names for all module global variables which are assigned required modules:

    ```js
    var Hapi = require('hapi');
  3. Eran Hammer revised this gist Feb 2, 2015. 1 changed file with 10 additions and 10 deletions.
    20 changes: 10 additions & 10 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    Whether you use 2 spaces or 4 spaces, there are a few simple things that can make your code easier to read. We've been using them in all the hapi modules for over 4 years now. This list is by no means complete but it highlights the more useful elements that will give you immediate value in reducing bugs in your code.
    Whether you use 2 spaces or 4 spaces, there are a few simple things that can make your node.js code easier to read. We've been using them in all the hapi modules for over 4 years now to great results. This list is by no means complete but it highlights the most useful elements that will give you immediate value in reducing bugs.

    ### Required modules

    @@ -23,7 +23,7 @@ This makes it trivial to identify which variables in your code represent require

    ### Module classes

    When a module (any node file) contains its own prototype class, that class variable also starts with an uppercase which can cause confusion. However, we do not allow any module global variable except for one: `internals`.
    When a module (any node.js file) contains its own prototype class, that class variable also starts with an uppercase which can cause confusion. However, we do not allow any module global variable except for one: `internals` (and of course whatever node.js provides).

    ```js
    var Hapi = require('hapi');
    @@ -36,7 +36,7 @@ internals.Server = function () {
    this.server.connection();
    };

    internals.server = new new internals.Server();
    internals.server = new internals.Server();

    // This is not allowed:

    @@ -45,25 +45,25 @@ var server = new internals.Server();

    This means that within any function in your code, uppercase variables are either required modules APIs or JavaScript natives. Any `internals` prefixed variables are module globals which act as a singleton. The rest are either function variables or arguments which are easier to spot because of the smaller scope.

    A note about singletons - I often see bugs caused by developers using a module global variable used by a module prototype. For example:
    A note about singletons - I often see bugs caused by developers using a module global variable used by a prototype. For example:

    ```js
    var internals = {};

    internals.cache = {};
    internals.storage = {};

    exports.Cache = internals.Cache = function () {

    };

    internals.Cache.prototype.get = function (key) {

    return internals.cache[key];
    return internals.storage[key];
    };

    internals.Cache.prototype.set = function (key, value) {

    return internals.cache[key] = value;
    return internals.storage[key] = value;
    };
    ```

    @@ -76,7 +76,7 @@ var cache1 = new Cache();
    var cache2 = new Cache(); // Uses the same memory cache1 uses
    ```

    By requiring the use of `internals` as a prefix, this bug becomes obvious to spot. When I code review a pull request, I always search for all the instances of `internals` to make sure it only holds static configuration and other safe data to share between instances. This is much harder to spot with out the prefix.
    By requiring the use of `internals` as a prefix, this bug becomes obvious to spot. When I code review a pull request, I always search for all the instances of `internals` to make sure it only holds static configuration and other safe data to share between instances. This is much harder to spot without the prefix.

    ### Callback new lines

    @@ -122,7 +122,7 @@ Allowing your eyes to immediately identify those line breaks means you can quick

    ### `return` callbacks

    You might have noticed that every `callback()` call is prefixed with `return`. This is a defensive programming tool. It provides both a visual cue where in the code execution ends and returned up the stack, but also protection against adding code after that point that can create timing issues, race conditions, or multiple callback calls.
    You might have noticed that every `callback()` call is prefixed with `return`. This is a defensive programming tool. It provides both a visual cue where in the code execution ends and returned up the stack, but also protection against adding code later after that point that can create timing issues, race conditions, or multiple callback calls.

    It is sometimes helpful to add a `return` when it is not obvious a nested function contains a callback. Consider the example above, it can be made safer with an extra `return` either in front of the method:

    @@ -168,7 +168,7 @@ internals.read = function (db, key, callback) {

    ### `callback` vs `next`

    There are many cases where a callback doesn't have to be called on the next tick for performance or workflow reasons. However, that can create an unpredictable API where it is not clear on which tick the callback is invoked. To make it explicit, I use `callback` when it is guaranteed the callback is called on another tick, and `next` when it can be both. It is a simple naming convention that has made maintaining hapi much simpler over many months.
    There are many cases where a callback doesn't have to be called on the next tick for performance or workflow reasons. However, that can create an unpredictable API where it is not clear in which tick the callback is invoked. To make it explicit, I use `callback` when it is guaranteed the callback is called on another tick, and `next` when it can be both. It is a simple naming convention that has made maintaining hapi much simpler over many months.

    ```js
    exports.a = function (a, b, callback) {
  4. Eran Hammer revised this gist Feb 2, 2015. No changes.
  5. Eran Hammer revised this gist Feb 2, 2015. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -96,6 +96,7 @@ internals.read = function (db, key, callback) {
    return callback(null, value);
    });
    };
    ```

    At a quick glance, it is not obvious that the `db.get()` method is asynchronous. It looks just like any other scope indentation. By adding a new line after every function declaration, we make the callbacks "pop" and much easier to spot:

    @@ -115,6 +116,7 @@ internals.read = function (db, key, callback) {
    return callback(null, value);
    });
    };
    ```

    Allowing your eyes to immediately identify those line breaks means you can quickly note where your code switched event loop ticks.

    @@ -140,6 +142,7 @@ internals.read = function (db, key, callback) {
    return callback(null, value);
    });
    };
    ```

    or immediately after (usually with a comment):

    @@ -161,6 +164,7 @@ internals.read = function (db, key, callback) {

    return; // db.get() invokes the callback
    };
    ```

    ### `callback` vs `next`

  6. Eran Hammer revised this gist Feb 2, 2015. 1 changed file with 104 additions and 1 deletion.
    105 changes: 104 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    Whether you use 2 spaces or 4 spaces, there are a few simple things that can make your code easier to read. We've been using them in all the hapi modules for over 4 years now.
    Whether you use 2 spaces or 4 spaces, there are a few simple things that can make your code easier to read. We've been using them in all the hapi modules for over 4 years now. This list is by no means complete but it highlights the more useful elements that will give you immediate value in reducing bugs in your code.

    ### Required modules

    @@ -77,3 +77,106 @@ var cache2 = new Cache(); // Uses the same memory cache1 uses
    ```

    By requiring the use of `internals` as a prefix, this bug becomes obvious to spot. When I code review a pull request, I always search for all the instances of `internals` to make sure it only holds static configuration and other safe data to share between instances. This is much harder to spot with out the prefix.

    ### Callback new lines

    Understanding your code flow is critical in asynchronous development. Consider this:

    ```js
    internals.read = function (db, key, callback) {
    db.get(key, function (err, value, meta) {
    if (err) {
    return callback(err);
    }

    if (meta.age > 10000) {
    return callback(null, null);
    }

    return callback(null, value);
    });
    };

    At a quick glance, it is not obvious that the `db.get()` method is asynchronous. It looks just like any other scope indentation. By adding a new line after every function declaration, we make the callbacks "pop" and much easier to spot:

    ```js
    internals.read = function (db, key, callback) {

    db.get(key, function (err, value, meta) {

    if (err) {
    return callback(err);
    }

    if (meta.age > 10000) {
    return callback(null, null);
    }

    return callback(null, value);
    });
    };

    Allowing your eyes to immediately identify those line breaks means you can quickly note where your code switched event loop ticks.

    ### `return` callbacks

    You might have noticed that every `callback()` call is prefixed with `return`. This is a defensive programming tool. It provides both a visual cue where in the code execution ends and returned up the stack, but also protection against adding code after that point that can create timing issues, race conditions, or multiple callback calls.

    It is sometimes helpful to add a `return` when it is not obvious a nested function contains a callback. Consider the example above, it can be made safer with an extra `return` either in front of the method:

    ```js
    internals.read = function (db, key, callback) {

    return db.get(key, function (err, value, meta) {

    if (err) {
    return callback(err);
    }

    if (meta.age > 10000) {
    return callback(null, null);
    }

    return callback(null, value);
    });
    };

    or immediately after (usually with a comment):

    ```js
    internals.read = function (db, key, callback) {
    db.get(key, function (err, value, meta) {
    if (err) {
    return callback(err);
    }
    if (meta.age > 10000) {
    return callback(null, null);
    }
    return callback(null, value);
    });
    return; // db.get() invokes the callback
    };
    ### `callback` vs `next`
    There are many cases where a callback doesn't have to be called on the next tick for performance or workflow reasons. However, that can create an unpredictable API where it is not clear on which tick the callback is invoked. To make it explicit, I use `callback` when it is guaranteed the callback is called on another tick, and `next` when it can be both. It is a simple naming convention that has made maintaining hapi much simpler over many months.
    ```js
    exports.a = function (a, b, callback) {

    return process.nextTick(function () {

    return callback(null, a + b);
    });
    };

    exports.b = function (a, b, next) {

    return next(null, a + b);
    };
    ```
  7. Eran Hammer revised this gist Feb 2, 2015. 1 changed file with 56 additions and 1 deletion.
    57 changes: 56 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -21,4 +21,59 @@ exports.add = function (a, b) {

    This makes it trivial to identify which variables in your code represent required modules. The language itself does contain a few uppercase variables but those are well known and should not cause any confusion.

    ###
    ### Module classes

    When a module (any node file) contains its own prototype class, that class variable also starts with an uppercase which can cause confusion. However, we do not allow any module global variable except for one: `internals`.

    ```js
    var Hapi = require('hapi');

    var internals = {};

    internals.Server = function () {

    this.server = new Hapi.Server();
    this.server.connection();
    };

    internals.server = new new internals.Server();

    // This is not allowed:

    var server = new internals.Server();
    ```

    This means that within any function in your code, uppercase variables are either required modules APIs or JavaScript natives. Any `internals` prefixed variables are module globals which act as a singleton. The rest are either function variables or arguments which are easier to spot because of the smaller scope.

    A note about singletons - I often see bugs caused by developers using a module global variable used by a module prototype. For example:

    ```js
    var internals = {};

    internals.cache = {};

    exports.Cache = internals.Cache = function () {

    };

    internals.Cache.prototype.get = function (key) {

    return internals.cache[key];
    };

    internals.Cache.prototype.set = function (key, value) {

    return internals.cache[key] = value;
    };
    ```

    The problem is that multiple instances of the cache will all share the same memory:

    ```js
    var Cache = require('./cache');

    var cache1 = new Cache();
    var cache2 = new Cache(); // Uses the same memory cache1 uses
    ```

    By requiring the use of `internals` as a prefix, this bug becomes obvious to spot. When I code review a pull request, I always search for all the instances of `internals` to make sure it only holds static configuration and other safe data to share between instances. This is much harder to spot with out the prefix.
  8. Eran Hammer created this gist Feb 2, 2015.
    24 changes: 24 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    Whether you use 2 spaces or 4 spaces, there are a few simple things that can make your code easier to read. We've been using them in all the hapi modules for over 4 years now.

    ### Required modules

    JavaScript makes is harder than most languages to know where variables are coming from. Variables assigned required modules are particularly important because they represent a singleton object shared with the entire application. There are also globals and module globals, along with function variables and arguments.

    Traditionally, variables starting with an uppercase letter represent a class that must be instantiated using `new`. This was an important semantic in the early days of JavaScript but at this point, if you don't know `Date` requires `new Date()` you are probably very new. We have adopted CamelCase variable names for all module global variables which are assigned required modules:

    ```js
    var Hapi = require('hapi');
    ```

    Note that you cannot `new Hapi()`, only `new Hapi.Server()`. In this style, the exported object from the modules always exposes an object which contains the API. This means a single function module should still export an object with the single method as an object property:

    ```js
    exports.add = function (a, b) {

    return a + b;
    };
    ```

    This makes it trivial to identify which variables in your code represent required modules. The language itself does contain a few uppercase variables but those are well known and should not cause any confusion.

    ###