I write 3 types of software

Single function modules, flat libraries and applications.

I have noticed lately that all my projects (and there are a lot of them, see for yourself at glebbahmutov.com) can be classified into 3 large groups. This division has evolved naturally over time, but was strongly influenced by the following two ideas

Single function modules

The great majority of my repositories published on NPM are modules that export a single function. Small partial application utilities like spots or larger pieces like ng-describe - they all have a tiny user surface API - just a single function.

I do think about the function's signature design a lot, trying to make a function both powerful and simple to use. If possible, I prefer exporting a function with a valid toString() representation. Anyone who does not have immediate access to the documentation (check out manpm) can still run fn.toString() to see the source code! For example, imagine I am writing a module called add-one and all it does is exports a function that returns the sum argument + 1. I could just take a regular addition function and partially apply first argument using lodash.partial

1
2
3
// BAD
var _ = require('lodash')
module.exports = _.partial(_.add, 1)

If I am using this function, I have to use the documentation to find how it works. I cannot simply look at the code

1
2
3
4
5
6
7
8
9
10
11
12
// user code
var add1 = require('add-one')
// what arguments does it expect?
console.log(add1.toString())
/*
function wrapper() {
var argsIndex = -1,
argsLength = arguments.length
...
return apply(fn, isBind ? thisArg : this, args);
}
*/

We are getting the wrapper source code instead of add1 logic. Here is how I would have exported the function for easy user inspection.

1
2
3
4
5
6
// GOOD
var _ = require('lodash')
function add1(a) {
return _.add(1, a)
}
module.exports = add1

A user can get meaningful information now

1
2
3
4
5
6
7
8
9
// user code
var add1 = require('add-one')
// what arguments does it expect?
console.log(add1.toString())
/*
function add1(a) {
return _.add(1, a)
}
*/

Modules with a single function are simple to write, test and modify. When the API changes, or a bug is fixed, we can use the semantic release in pretty much automated mode to avoid breaking the existing users.

Flat libraries

My next set of tools are collections of related functions in a flat library. A flat library has every function available right away usually as a property on the exported object. Good examples are my collections of utilities to deal with NPM npm-tools and Git ggit.

Whenever one returns an object with methods in JavaScript, one must be careful not to use this variable inside the methods. Otherwise the user has to call the method using the object reference, or perform context binding, and that is extra thing the user does not need to worry. For example, if we export an object with add and add1 methods, I don't like using this.add from inside the add1 method

1
2
3
4
5
6
7
8
9
10
// BAD - adder module
module.exports = {
add: function (a, b) { return a + b },
add1: function (a) { return this.add(a, 1) }
}
// user code
var adder = require('adder')
console.log(adder.add1(10)) // 11
console.log([1, 2, 3].map(adder.add1))
// CRASH AND BURN

Instead, I prefer to export an object that only refers to plain functions.

1
2
3
4
5
6
7
8
9
10
// GOOD - adder module
function add(a, b) { return a + b }
function add1(a) { return add(a, 1) }
module.exports = {
add: add,
add1: add1
}
// user code
console.log(adder.add1(10)) // 11
console.log([1, 2, 3].map(adder.add1)) // [2, 3, 4]

Flat libraries of functions are simple to test, use and even document, check out xplain

Applications

The last type of modules I publish are (mostly CLI) applications. Ideally, these application are just thin argument parsers on top of other modules (flat libraries and single functions). I try to keep the number of source files inside the application small and flat. If the number of source files inside src folder has grown to more than five files, it is a red flag - time to factor out some code to its own separate module.

Of course, sometimes I break my own and The Twelve-factor app rules. Most often this happens when I have a working application and then I come up with another application that scales it up. For example, my dependency testing and upgrade CLI application next-update deals with a single package at a time. I wrote next-updater that can update multiple packages, for example all my NPM modules one after another. So I placed the main functionality in next-update into main: index.js file and then used it as a dependency in the next-updater module. This goes against the 12 factor Codebase principle because we build both the app and the library from the same code base. Eventually I will refactor the common feature into its own module (and this module will probably export a single function!)

There a couple of utilities I use a lot in my applications