Wednesday, August 9, 2017

Practical JavaScript Nomenclature

Universal Nomenclature Standards for Variables and Functions

This is a set of rules I set for our internal JavaScript style guide after finding nothing similar online. Having consulted a few of my colleagues and used these extensively myself, I stand by these guidelines and use them every day. I hope you find these either helpful or at least controversial. These rules are optimized for clarity and readability above all. They favour the code reviewer or code maintainer over the code author. Eventually, we are all the maintainer.

Be Clear

Use Good Grammar

Be Specific

Be Consistent

Examples

Ambiguous Booleans

A variable called clearSearch is ambiguous. It could specify whether a search is clear, or it could specify whether a search should be cleared, etc. Using an auxiliary verb makes them clear. Choose names like isSearchClear and shouldClearSearch, for example.

Specific Meaning

Variables that have a specific meaning should be called with their assigned meaning as soon as possible, and they should lose meaning when appropriate. Consider this example:

var coordinates = POSITIONING.getUserCoordinates();
return checkCoordinates( coordinates );

function checkCoordinates( userCoordinates ) {
  return userCoordinates.areWithin( CITIES.Toronto );
}

The names of the variables do not reflect their specific meanings. The method getUserCoordinates returns userCoordinates by definition. The function checkCoordinates checks a coordinates variable, by definition. Inside that function, coordinates don’t have any meaning aside from just being coordinates. Also, the verb check is too vague for this case. A better version:

var userCoordinates = POSITIONING.getUserCoordinates();
return areCoordinatesWithinToronto( userCoordinates );

function areCoordinatesWithinToronto( coordinates ) {
  return coordinates.areWithin( CITIES.Toronto );
}

Signatures and Arguments

Here’s an example from an API wrapper:

function getProductByID( parameters ) {
  return $http.get( '/product', parameters );
}

This is a good example of function signatures not matching their parameters. A function called getProductByID strongly implies that it accepts an id parameter, which it should:

function getProductByID( id ) {
  return $http.get( '/product', { productID: id });
}

Long Names and Ambiguity

These guidelines heavily favour long variable and function names, but there’s a pretty natural limit. Function names that have more than 5 major parts of speech (nouns, verbs, and adjectives) are candidates to be renamed. Names like createMembershipPermissionGetter (4 major parts of speech) or checkIfRecipesAreLiked (4 major parts of speech) are still fine, but anything longer needs to be renamed, especially if it’s the part of a public API for a component.

Renaming long functions requires some creativity to find a good name for the overall operation. Oftentimes, it’s also a sign that some refactoring is in order, to group common operations together. For example, consider this structure:

<div
  class="range"
  ng-repeat="range in $ctrl.ranges"
  ng-if="$ctrl.checkIfTimeSlotIsEnabledIsAvailableAndIsWithinRange( range )"
>
  { range.start } — { range.end }
</div>
function checkIfTimeSlotIsEnabledIsAvailableAndIsWithinRange( range ) {

  if ( range.status === 'unavailable' ) return false;
  if ( !range.enabled ) return false;

  var
    now = moment();

  if ( now.isBefore( range.start ) ) return false;
  if ( now.isAfter( range.end ) ) return false;
}

This name is too verbose (5 major parts of speech, by a conservative count). The best way to figure out a new name is to figure out how the result of the function is used, rather than what it does. Since this function is used to determine whether a range should be shown, shouldRangeBeShown is a good candidate for its name.