Friday, January 10, 2020

Documentation: The Good Parts

Writing documentation is probably the most boring part of software development. As developers, many of us avoid it. We write docs only when someone else force us, twist our arms or assign us JIRA tickets.
- Was it the project manager, or maybe that senior tech lead guy with the massive beard that made you write the docs?
What I want to describe in this post isn't complex Word documents explaining some software. It isn't a guide on how to write something clever about a public FirstName property of a Person class.

It's about JavaScript and data types. Keeping it simple, and fun.

 

https://commons.wikimedia.org/wiki/File:Unofficial_JavaScript_logo_2.svg
JavaScript is dynamic
JavaScript won't stop you from passing in whatever you want to a function, even when the code is expecting data of a specific type. It will crash during runtime instead, yes. When parsing or iterating something that cannot be parsed or iterated.

- No type checks for you!
This is also what makes the language very flexible. Simplistic syntax, functions as first class citizens and with a little less noise. There is no need for explicit data type declarations, generics or interfaces, making it easy to write code in less than 100 characters width. In fact, you will find yourself writing code in less than 80 chars quite often. In addition, having only one thread to worry about is a good thing (for most of us). And I believe the V8 engine is fast enough too.
- It would be nice if there was something that could let us have a little bit of both.

Did you say TypeScript?
TypeScript is cool. But I'm not sure it is the way to go for back-end code written for Node.js. Node.js itself is probably a bad choice if you are in great need of strongly typed language features on the server. Why not go for C# instead? 


If your code lives in the browser, well, that is something different. You are basically stuck with JavaScript for web development - whether you like it or not. TypeScript, or WASM, can probably help you - after some initial setup, headache & build scripts.

 

Have you tried JSDoc?
During my work on the Open Source project node-zookeeper, I have learned how JSDoc can help us make the JavaScript Developer Experience a lot better - without the headache. Your editor will give you hints and warnings about parameters and data types.

Here's an example:

/** @returns {number} */
function getRandom () {
    ...
}


With parameters:

/**
 * @param {number} minValue
 * @param {number} maxValue
 * @returns {number}
 */

function getRandom (minValue, maxValue) {
    ...
}

 
In the example, documentation is used only for describing the data types of input parameters and the return type. Nothing else. I would recommend you to try hard and refactor the actual code, making additional comments not needed. Is the function name getRandom good enough?

- Maybe I should add some comments to the code here?
(don't do it)

Your editor is smart
Your IDE or editor will figure out the JSDoc syntax, even for custom type definitions. Custom type definitions are for objects - custom data types - that cannot be described as built in strings, booleans, numbers, objects or arrays.

Another example, this one is from the typedefs.js of the node-zookeeper project, describing a ZooKeeper node changed callback:


/**
 * Watch callback
 * @typedef {callback} watchCb
 * @param {number} type
 * @param {number} state
 * @param {string} path
 */
 


The type definition for the watcher can be referenced in the code like this:
/** @param {watchCb} callback */

The node-zookeeper project has a typedefs.js file in the root folder. A smart IDE, like WebStorm, will parse it and use the type definitions to give you and your coder friends a nice developer experience.



When dynamic isn't good enough, use JSDoc. But be careful, use it only when needed. Or else, you will be stuck in the Documentation Swamp.

Sunday, January 5, 2020

Experimental Node Mode

The latest Long-Term Support (LTS) version of Node.js has support for writing and consuming modules in ECMAScript style - without any tools or frameworks. That is good news! I have mostly been writing back-end code using the CommonJS standard, but have always preferred the ECMAScript way, commonly used in front-end development with tools like Babel.

Now, with Node.js 12, there is native support for ECMAScript modules. But it has to be manually enabled, by using the '--experimental-modules' flag.

The current version - Node.js 13 - doesn't need a flag to enable ECMAScript modules. Great! It is on by default. But the runtime will log a message on startup, a warning that the feature is not ready for production yet. Maybe we can expect it to be a default setting, without experimental warnings, in the next LTS later this year?

I have tried out ECMAScript modules in Node.js, using the experimental flag and the type: module in package.json.


WebStorm IDE with ES Module support
Import tooling support, not perfect, but good enough.

Cool, but not perfect yet
I ran into issues when writing & running unit tests. My favorite unit testing library is tape, but it doesn't support ECMAScript modules natively:
Must use import to load ES Module

What about the AVA test runner ? No native support yet. Mocha? I couldn't find any info about it, but tests won't run.

Tape is just JavaScript 👍
It is convenient, but tape unit tests doesn't require the built in runner. Both AVA and Mocha need a runner. Using tape, you can run the unit tests just like any other JavaScript file with the Node.js CLI.

Don't try this at ... work?
Besides that using a default unit testing runner usually is a good thing, there's also a huge drawback with the experimental & non-standard approach that I describe in this post. The node command will run a single file only. 👀 

But, hey, we're in Experimental Mode right! A workaround would be to execute a custom runner containing all our unit tests. Just don't forget to add every new file to it.

"scripts": {
    "test": "node --experimental-modules test/run.js | tap-spec"
}

(from the script section of package.json)

The test/run.js file:
import 'path-to-unit-test'
import 'path-to-another-unit-test'


Try this at home, folks. Don't try it at work (yet). 🏡