CoffeeScript Styleguide¶
This guide should give an overview of the general style within the alinex system and it's packages. It is neither meant as a hard limit but neither as some nice to have rules. So as much as possible this rules have to be followed.
General layout¶
Within the code I use 2 spaces for indention neither tabs because they make reading problematic and also are not suited for coffee script if mixed with spaces.
Naming¶
Variables¶
Over all modules some common variable names will be used for the same values:
cb
- for the callback methoddone
- alternatively used for the callback for better readingerr
orerror
- for an error message object
Classes start with an Uppercase letter:
test = new Test
To mark private properties/functions they may start with an underscore.
Modules are always imported into a variable which is named after the module and if the module exports a class the uppercase name is used.
Requirements¶
Generally required modules belong on top of the file. But all things which will be required at the top will immediately load and slow down the initialization. Often some of these requirements won't be needed in every call. So it is better to load them dynamically then needed.
To optimize readability the variables for dynamically loaded requirement will be set on top and the real requirement can be put anywhere in the code.
Classes and Instances¶
Classes are written starting with uppercase letter while it's instances start with lowercase letters.
Constants¶
They are used seldom but if so you have to name it completely in uppercase with underscores for word separator.
Asynchronous code¶
Through the whole code as much as possible only asynchronous code should be used without blocking. That brings the most performance out.
Callbacks¶
These code follows the node.js convention of providing a single callback as the last argument of your asynchronous function.
To not make it too complex the async function may call it's callback synchronously in the same event loop or later. It is no true asynchronity by definition, so if you need it maybe think about your code structure or call the function through process.nextTick()
yourself.
Async module¶
You may also use the Async module. It brings you a lot of methods to make asynchronous calls parallel or serial.
Events¶
Often events are also used to make use of the asynchronous behavior in class objects.
Error/Exception handling¶
Error and exception handling will be done in JavaScript/CoffeeScript in the following ways:
- Synchronous code will return an Error object instead of the normal return value:
myfuncSync = () ->
x = doSomething()
if x is 1
return new Error "Failed calculation"
return x
result = myfuncSync()
if result instanceof Error
console.error result.message
- Asynchronous code should send the error as first parameter to the callback method or
null
if no error occurred:
myfunc = (cb) ->
x = doSomething()
if x is 1
cb new Error "Failed calculation"
else
cb null, x
myfunc (err, result) ->
if err
console.error err.message
-
And at last an error may be thrown only if it is a hard error which signals an fatal error which may stop the overall process.
Such an error may be catched in a try block, in a domain or on the process.
-
Another solution is to use eventful methods which emit an
error
event
Read more in the errorHandler documentation.
Control flow¶
To make the code more readable and prevent some hanging else-statements which you have to search to which indention level it belongs to return or callback as fast as possible.
Bad:
if err
doSomething (err) ->
return cb err
else
return true
Good:
return true unless err
doSomething (err) ->
return cb err
Documentation¶
All the code in any language will be documented inline using comments as possible in the specific language. No JavaDoc or alike have to be used. Only plain markup which will be automatically detected and converted to documentation.
In each exported method the parameters will be documented in detail and the callback methods with their values, they will get.
Some overall documentation like this will be stored in special markup files ending with .md
.
Debugging¶
Best use the node-inspector for this:
sudo npm install -g node-inspector
node-inspector
node --debug-brk lib/cli.js -v
But additionally the debug
package is used in all packages to make it possible to debug any time, also in the production code to get more information.
debug = require('debug') 'mypack'
debug "the system is running"
You instantiate a new debug instance after requiring it with a name. This should be your package name (for the main part). For subparts of your package add a colon with a specifier like mypack:reader
or mypack:writer:html
.
If you run the code above normally nothing will happen but if you set the DEBUG
environment variable like:
DEBUG=mypack node lib/index.js
You will get the debug output. You can add multiple debug packages with colon or use asterisk like mypack*
to select the package with all sub level.
You can also add multiple debug statements and exclude some:
DEBUG=config*,exec*
DEBUG=*,-config*
Testing¶
Linting is used to pre-check the code. This will not only check the syntax but also the style and the above conventions of the styleguide.
Mocha unit tests are used to check individual parts and the overall functionality. As much as possible should be tested but not only single units but all interface calls. An test coverage of 100% should be the goal.
If used the builder will call them with builder -c test
for you.
Publishing¶
To publish new packages the following steps should be done:
- Check dependencies and their version in package.json (maybe run
npm outdated
) - Cleanup to remove test packages
- Install and update packages
- Run the test suite
- Push code to repository
- Publish new package
- Create and publish new documentation
The tasks are done in an easy way using the alinex-builder.
# check what is changed and if all packages are up-to-date
builder -c changes
# do the steps 1-7
builder -c publish --minor
Configuration¶
The general configuration is stored in files to be also available without database connection. To make it easy readable and maintainable it should be written in YAML rather than JSON.
alinex-config helps in working with such files.
Dependencies¶
To use singleton modules the dependencies for specific versions should be as open as possible. This makes it possible to use:
npm dedupe
This will flatten down the modules to the uppermost common module in hierarchy.
Peer dependencies are also possible, in which a sub module specifies which parent it needs. This will be specified in the peerDependencies
section.