Leaving examples in code

Leave executable examples inside source files for future reference.

Whenever I write any code my goal is to make it as readable and easy to understand as possible. A computer can understand my code as long as it is syntactically correct, but other programmers (and I myself) can only understand the code if it is clear, focused and documented.

Small and simple modules are easy to document using a single README markdown file, even if including unit tests as examples. Larger projects with multiple separate features might be harder to explain. Even the author of the project might forget what each step of a complicated algorithm involves. To alleviate this problem I use two approaches.

Runnable stand alone code

I leave runnable code inside each CommonJS file. Typically a CommonJS file only exports a main function / object used somewhere else in the main algorithm. To remember what this particular code does I leave executable code inside a guard block.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/complex-something.js
var debug = require('debug')('example');
function complexSomething() {
// lots of code
debug('complex something foo is', foo);
// more code
}
module.exports = complexSomething;
if (!module.parent) {
(function complexSomethingExample() {
/* eslint no-unused-vars:0 */
function complexSomethingExample1() {
// use complexSomething in some way
debug('complex something produced', ...);
}
function complexSomethingExample2() {
// use complexSomething in some other way
debug('complex something produced', ...);
}
complexSomethingExample2();
}());
}

This way, the module is normally called from another module, thus the code inside if (!module.parent) { ... } does not execute. Whenever I need to remember what this particular code produces, I just run it by itself

DEBUG=example node src/complex-something.js

There might be multiple examples inside the guarded code block: complexSomethingExample1, complexSomethingExample2, and I prefer to give them descriptive names. I also use either debug or debug-logdown to print messages only when running as a stand alone code (by setting the environment variable DEBUG=example).

An example of runnable blocks can be found in bahmutov/changed-log repo inside files src/get-comments-between-commits.js and src/report.js.

Example NPM scripts

I usually have the default npm test script run just the unit tests. In addition I write script commands to show basic usage of an application or library. For example, bahmutov/changed-log has a couple of such scripts in its package.json

1
2
3
4
5
6
"scripts": {
"test": "mocha -R spec src/**/*-spec.js",
"watch": "mocha -R spec --watch src/**/*-spec.js",
"example": "node bin/changed-log.js next-update 0.8.0 0.8.3",
"chalk": "node bin/changed-log.js chalk 0.3.0 0.5.1"
}

The command npm run chalk produces the output I included in the changed-log/README, while npm run example is another shorter example from a repository I control. Having these examples makes it simpler for me to go back and resume coding the changed-log tool after a few days or even months of inactivity.

Conclusion

Having examples in each file is much simpler than unit testing a piece of code, or fully describing its purpose and expected inputs / outputs. A few larger use cases can be coded inside the scipts section of a package.json file, showing the main command line switches in action.