Skip to content

Best-practices and coding conventions for the CoffeeScript programming language

Notifications You must be signed in to change notification settings

bluespeckfinancial/coffeescript-style-guide

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 

Repository files navigation

CoffeeScript Style Guide

This guide presents a collection of best-practices and coding conventions for the CoffeeScript programming language.

This guide is intended to be community-driven, and contributions are highly encouraged.

Please note that this is a work-in-progress: there is much more that can be specified, and some of the guidelines that have been specified may not be deemed to be idiomatic by the community (in which case, these offending guidelines will be modified or removed, as appropriate).

Inspiration

The details in this guide have been very heavily inspired by several existing style guides and other resources. In particular:

Table of Contents

## Code layout ### Tabs or Spaces?

Use spaces only, with 2 spaces per indentation level. Never mix tabs and spaces.

### Maximum Line Length

Limit all lines to a maximum of 100 characters.

### Blank Lines

Separate top-level function and class definitions with a single blank line.

Separate method definitions inside of a class with a single blank line.

Use a single blank line within the bodies of methods or functions in cases where this improves readability (e.g., for the purpose of delineating logical sections).

# Bad
class Model extends Base
  set: (data) -> _.extend(@data, data)
  save: (data) -> @set(data) and @sync("save", @data)
  
# Good
class Model extends Base

  set: (data) ->
    _.extend(@data, data)
    
  save: (data) ->
    @set(data)
    @sync("save", @data)
### Trailing Whitespace

Do not include trailing whitespace on any lines.

### Optional Commas

Avoid the use of commas before newlines when properties or elements of an Object or Array are listed on separate lines.

# Yes
foo = [
  'some'
  'string'
  'values'
]
bar:
  label: 'test'
  value: 87

# No
foo = [
  'some',
  'string',
  'values'
]
bar:
  label: 'test',
  value: 87
### Encoding

UTF-8 is the preferred source file encoding.

## Module Imports

If using a module system (CommonJS Modules, AMD, etc.), require statements should be placed on separate lines.

require 'lib/setup'
Backbone = require 'backbone'

These statements should be grouped in the following order:

  1. Standard library imports (if a standard library exists)
  2. Third party library imports
  3. Local imports (imports specific to this application or library)
## Whitespace in Expressions and Statements

Avoid extraneous whitespace in the following situations:

  • Immediately inside parentheses, brackets or braces

       ($ 'body') # Yes
       ( $ 'body' ) # No
  • Immediately before a comma

       console.log x, y # Yes
       console.log x , y # No

Additional recommendations:

  • Always surround these binary operators with a single space on either side

    • assignment: =

      • Note that this also applies when indicating default parameter value(s) in a function declaration

        test: (param = null) -> # Yes
        test: (param=null) -> # No
    • augmented assignment: +=, -=, etc.

    • comparisons: ==, <, >, <=, >=, unless, etc.

    • arithmetic operators: +, -, *, /, etc.

    • (Do not use more than one space around these operators)

         # Yes
         x = 1
         y = 1
         fooBar = 3
      
         # No
         x      = 1
         y      = 1
         fooBar = 3
## Comments

If modifying code that is described by an existing comment, update the comment such that it accurately reflects the new code. (Ideally, improve the code to obviate the need for the comment, and delete the comment entirely.)

The first word of the comment should be capitalized, unless the first word is an identifier that begins with a lower-case letter.

If a comment is short, the period at the end can be omitted.

### Block Comments

Block comments apply to the block of code that follows them.

  ###
  This is a block comment. Note that if this were a real block
  comment, we would actually be describing the proceeding code.
  
  This is the second paragraph of the same block comment.
  ###

  init()
  start()
  stop()
### Inline Comments

Inline comments are placed on the line immediately above the statement that they are describing. If the inline comment is sufficiently short, it can be placed on the same line as the statement (separated by a single space from the end of the statement).

All inline comments should start with a # and a single space.

The use of inline comments should be limited, because their existence is typically a sign of a code smell.

Do not use inline comments when they state the obvious:

  # No
  x = x + 1 # Increment x

However, inline comments can be useful in certain scenarios:

  # Yes
  x = x + 1 # Compensate for border
## Naming Conventions

Use camelCase (with a leading lowercase character) to name all variables, methods, and object properties.

Use CamelCase (with a leading uppercase character) to name all classes. (This style is also commonly referred to as PascalCase, CamelCaps, or CapWords, among other alternatives.)

(The official CoffeeScript convention is camelcase, because this simplifies interoperability with JavaScript. For more on this decision, see here.)

For constants, use all uppercase with underscores:

CONSTANT_LIKE_THIS

Methods and variables that are intended to be "private" should begin with a leading underscore:

_privateMethod: ->
## Functions

(These guidelines also apply to the methods of a class.)

When declaring a function that takes arguments, always use a single space after the closing parenthesis of the arguments list:

foo = (arg1, arg2) -> # Yes
foo = (arg1, arg2)-> # No

Do not use parentheses when declaring functions that take no arguments:

bar = -> # Yes
bar = () -> # No

In cases where method calls are being chained and the code does not fit on a single line, each call should be placed on a separate line and indented by one level (i.e., two spaces), with a leading ..

[1..3]
  .map((x) -> x * x)
  .concat([10..12])
  .filter((x) -> x < 11)
  .reduce((x, y) -> x + y)

When calling functions, choose to omit or include parentheses in such a way that optimizes for readability. Keeping in mind that "readability" can be subjective, the following examples demonstrate cases where parentheses have been omitted or included in a manner that the community deems to be optimal:

baz 12

brush.ellipse {x: 10, y: 20} # Always use braces around inlined object parameters

# Alternatively, pass in objects on a new line (without braces)
brush.ellipse
  x: 10
  y: 20

foo(4).bar(8)

obj.value(10, 20) / obj.value(20, 10)

print inspect value

new Tag(new Value(a, b), new Arg(c))
## Strings

Use string interpolation instead of string concatenation:

"this is an #{adjective} string" # Yes
"this is an " + adjective + " string" # No

Prefer double quoted strings ("") instead of single quoted ('') strings

## Conditionals

Favor if not over unless for negative conditions.

Instead of using if not...else, use if...else:

  # Yes
  if true
    ...
  else
    ...

  # No
  unless false
    ...
  else
    ...

Multi-line if/else clauses should use indentation:

  # Yes
  if true
    ...
  else
    ...

  # No
  if true then ...
  else ...

Avoid placing the if clause at the end of the line

  #Yes
  if isReady then start()
  
  #No
  start() if isReady
## Looping and Comprehensions

Use functional style code instead of language based iteration where possible:

  # Yes (assuming you have ramda available)
  result = R.pluck 'name', array
  
  #No
  result = (item.name for item in array)

  # No
  results = []
  for item in array
    results.push item.name

To filter:

# Yes
result = R.filter ((item) -> item.name is "test"), array

# No
result = (item for item in array when item.name is "test")
## Extending Native Objects

Do not modify native objects.

For example, do not modify Array.prototype to introduce Array#forEach.

## Exceptions

Do not suppress exceptions.

## Miscellaneous

and is preferred over &&.

or is preferred over ||.

is is preferred over ==.

not is preferred over !.

Prefer shorthand notation (::) for accessing an object's prototype:

Array::slice # Yes
Array.prototype.slice # No

Prefer @property over this.property.

return @property # Yes
return this.property # No

However, avoid the use of standalone @:

return this # Yes
return @ # No

Avoid return where not required, unless the explicit return increases clarity.

Use splats (...) when working with functions that accept variable numbers of arguments:

console.log args... # Yes

(a, b, c, rest...) -> # Yes

About

Best-practices and coding conventions for the CoffeeScript programming language

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published