Primitiveness is Bliss
New package? Use the first item below that provides just enough to get your job done. If you’re between two choices, shrink the job to fit one size down.
- A JSON data file. Yes, you can do this. Node.js will require it just fine. npm will package it with
package.json. Use a
prepublishscript to generate the JSON if you like.
- A single function that takes arguments and returns a useful object.
- A single function that takes arguments and mutates one of them. Mention what gets worked over in
- A single function that takes arguments and a callback.
- An object constructor function with methods on its prototype.
- An object constructor function for an event emitter.
- An object constructor function for a stream.
- A function or object that apes the interface of a related Node.js core module, like
fun().update(a, enc).digest(enc)for a hash function.
- An object whose only properties are functions described by (2), (3), and (4). Publishing this kind of thing without a
READMEis asking for trouble.
- Are you sure?
Stick to the Obvious
Whenever possible, use functions of plain
Object maps and
Array lists of
null—stuff you can round-trip with
JSON.parse(JSON.serialize(data)). If you really need binary,
Avoid esoteric and cutting-edge language features whenever possible. The latter include:
- any creative use of
- superficially appealing trivia, like
Object.freeze, truthiness quirks, and loose equality
- inheritance deeper than one programmer-defined prototype
- returning or yielding data structures peculiar to a third-party library your users aren’t likely to know well already
undefinedspecific meaning in context
- any new language feature you have not seen someone else use tastefully in boring, important code
Name Things Obviously
I am many times guilty of “clever” package naming. And I have done the time…picking through my own profile pages trying to remember what I called my own code.
Fortunately, none of those ill-named packages are in much use by anyone else. That would have put every nonnative speaker of English within their rights to sock me in the stomach. All for names that didn’t turn out very clever, after all.
There is an alternate universe where folks use npm, but everyone uses descriptive package names in their own namespace. Things are better there. Did I mention everybody sets
Name Things, Obviously
Give variables and functions descriptive names. People are very good at learning and using words and phrases. Make opportunities to name more code.
If you catch yourself “translating” a concept into a long boolean expression, method call chain, or numerical computation, type
var and a summary first. Modern runtimes are not going to punish you for lifting an
if statement’s condition out into a variable. Nor will they help you remember what
n does, or
buf is meant to buffer.
function is a painfully long keyword that makes a made dash for your column limit every time you type it. But seriously consider naming every function you write, even arguments to higher-order functions like
.filter() and friends. Break to a new line if you have to. The most likely victim of failure here is you.
Maybe coders you idolize use stubby, mean-nothing variable names. They may have faced a language barrier, or come up in a professional or academic context where short-name conventions were stronger. Maybe you fear, as I once did, that approachable code opens your work mode widely to criticism. You’ll be fine. Writing code that’s worth criticizing is the challenge.
The Async Gets in Everything
If you export a function that takes a function argument, someday, somebody, somewhere is going to want to pass a function that takes a callback.
Be kind to that person. It’s probably future-you.
The Streams Must Flow
If you export a function that takes an argument of variable size for storage, transformation, or reduction, someday, somebody, somewhere is going to want to stream that data in.
Don’t beat yourself up if you have to read the readable-stream doc again. Everybody does it. It’s a thing.
Nobody Wants More Files
For packages, the fewer files, the better. More than one Node.js module in a package is a warning sign.
This rule doesn’t apply to projects that are really about gluing lots of other packages together. There may be too much “glue” for one file, as there is in most website and back-end server projects. If you’ve got squeeze-out, go ahead, clean it up.
There’s no avoiding files for some features and services. If you want to use Travis CI, you need a
.travis.yml. If you want npm to
PATH a bin script, make a separate file with shebang. Separate your tests, because nobody wants to load those when they
require() your code or pack their browser bundle. Finally, some community conventions, like
LICENSE files, are worth honoring.
Resist configuration file accretion. Express your personal preferences in your home directory and standards that apply to all involved—style checkers, tests, and coverage tools—in your test scripts. Use
package.json and global
$HOME/.gitignore for common tooling byproducts. Try a style checker without configuration, or stop caring about code style to begin with. Set code coverage thresholds via command-line flags in run scripts.
When your eyes no longer jump right to the filename you need, start asking what can go. Some things were probably “fashionable” when the cruft piled up. Have they held up? If you need a pager to read
git ls-files for your package, you’re hoarding.
(Side Note: I’ve put test suites for many of my packages, like spdx-expression-parse, right in the
READMEs, literate programming style. Tools pull the code out and pipe to Node.js. So far, so great, but I’m not yet ready to recommend it universally.)
Use a Free CI Service
I use Travis CI, mostly via the Ruby CLI. I like it very much.
The important thing is to make sure some remote machine is testing your code from a blank slate on multiple runtimes, without the dubious benefits of your local working tree,
node_modules, and configuration. There is nothing you can do to completely stop screwing this stuff up, so have an automaton mock you discreetly when it happens again.
Say “No” to Semantically Meaningless SemVers
Semantic Versioning is swell. It gives meaning—ehem, semantics—to version numbers.
Except major version zero. Major version zero is a put-on:
Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable. — Semantic Versioning 2.0.0, section 4
Do not user major version zero. Just go straight to
1.0.0, and expect to hit
2.0.0 the same day. You can have as many version bumps as you want. Ain’t nothing wrong with double-digits.
npm version makes this easy.
npm i -g np makes it painless. You can even get away without release notes if you’re sneaky…but you didn’t hear it from me.
To so many friends and fellow programmers who’ve taught me so much these past few years: Thank you. These are my words, but they’re ideas that belong to many, perhaps least to me. The mistakes and blind alleys are mine. Let me know!
I look forward to chuckling at this a little further on down the line…
Your thoughts and feedback are always welcome by e-mail.
more articles — revision history — back to top