Tom MacWright

tom@macwright.com

State of documentation.js

I’ve been taking a break from actively maintaining documentation.js, to get some perspective and to see if anyone steps up to fill the gap. Here’s where that’s at:

Project health

Besides being a truly top-notch game for Mac OS, escape velocity is how I think of sustainability in open source. There’s a point at which there are enough contributors that any one contributor can leave and the basic health indicators - issues, outstanding pull requests, and so on - stay healthy.

When I work on projects at this point, I’m trying to hit one of a few equilibrium points:

  1. Small projects with tight enough scopes that they don’t need much continuing development and don’t have the potential for many different types of bugs.
  2. Large projects with broad enough audiences that they might attract enough contributors to become sustainable, thus offsetting their required continuing development & bug-fixing.

Documentation.js is definitely in the latter: it’s a big project, too much for one person to work on indefinitely solo, and a very broad audience, JavaScript developers.

Unfortunately, it isn’t at an equilibrium point. Though I’ve had wonderful contributors, I’m still pulling the vast majority of the hours of the project. I hope this changes at some point: there are certainly users, judging by the download statistics, steady stream of issues, and so on. But the conversion rate of these users to contributors is near zero. Almost nobody who finds an issue is interested in helping fix it.

There are plenty of community-building tactics here, many of which I’ve already done: instituting and enforcing a Code of Conduct, eagerly granting maintainer access to contributors, tagging help wanted issues, being exceptionally nice to people who report bugs, and so on.

There’s always more to do, and I’m incredibly open to suggestions of what more to do - tell me on Twitter or email me if you have ideas.

Prior art

I really care about research, prior art, and credit, so I’ve been curating a long list of other documentation solutions for JavaScript. The list keeps getting longer.

A summary would be: documentation syntax for JavaScript is a minefield of incompatible syntaxes.

Even just Google’s efforts amount to 3 separate styles: they largely support JSDoc’s maintenance by Jeff Williams, but also promote a version for Angular and a separate flavor for Clojure Compiler.

And, unfortunately, these syntaxes blend into each other: people writing Angular-flavored JSDoc comments don’t always know that they’re using Angular style - they identify with JSDoc instead, and expect compatibility. This extends out in many directions, and makes expectation management tricky, to say the least.

Then there are syntaxes like getdocs and Leafdoc that aim for brevity, and more tweaks to JSDoc like YUIDoc.

Pain points

Where documentation.js really starts to get tricky is the intersection of its ability to infer facts about code, using Flow type annotations, and its ability to handle documentation comments. Unfortunately, Flow can express types that JSDoc can’t, and vice-versa.

My original approach was to meld the human and the computer together, thus making it possible to write ES2017+Flow style new JavaScript, and also write old-fashioned ES5 JavaScript with more extensive comments:

// This is old-fashioned JavaScript, with explicit JSDoc:

/**
 * This squares the number
 * @param {number} x
 * @returns {number} squared
 */
function square(x) {
  return x * x;
}

// This is new-fashioned JavaScript, with inferred documentation:
/**
 * This squares the number
 */
function square(x: number): number {
  return x * x;
}

Unfortunately, JSDoc and Flow aren’t mirror images of each other: JSDoc isn’t a type system, much less a complete explanation of types. Some Flow concepts don’t exist in JSDoc, like intersection types. Some JSDoc types don’t exist in Flow, like non-nullable types. Some JSDoc concepts also don’t exist in Flow, like the ‘inner’ scope, which isn’t quite a JavaScript concept.

There are also issues with the JSDoc specification, which is written only as a user guide. It’s better documentation than most, but the user-guide style means that it isn’t quite precise enough to serve as raw material for writing implementations - there are points of ambiguity, like precedence and undocumented but widespread behavior. Due to the nature of JSDoc comments, certain things might be impossible, like using decorators in examples or using comments in examples. And, unfortunately, the jsdoc3.github.com project, the main authority for these questions, has been rather quiet.

Mea Culpa(s)

Some of the issues with documentation.js are my doing.

For instance, using module-deps to find all module dependencies quickly won the project that ability, but at a high performance cost - almost half the time spent generating documentation is used up by finding dependencies.

The way that documentation.js traverses the Babel AST should have been much smarter: right now it extracts comments and then runs inference on each comment to determine its context, like if it sits inside of a class. Instead, I should have implemented this kind of context tracking on a single downwards pass.

I also made a mistake by using simple iteration instead of proper recursive tree datastructures for parameters and member nesting. I was in a rush for the first implementation and after refactoring parameter nesting with a recursive tree, realize that doing it right the first time would have saved everyone a lot of time.

JavaScript

I choose projects that teach me things deeply, and documentation.js taught me JavaScript, again. It’s been great for that purpose. That said, my envy for other languages - with their well-established and robust defaults for documentation, like godoc, rustdoc, elm docs, and more - only increases the more I work on documentation.js.

I really like JavaScript - don’t get me wrong. It’s a near-universal language with an incredibly smart community and has been flexible enough to adopt a lot of great ideas over time.

But the smart, modern ideas are layered on top of years of failed experiments and legacy cruft, and as an author of tools, you have to deal with all of that. It’s not a great position to be in, and makes you pine for languages that started with good practices, rather than evolved to them.

Plans

I’ll consider dropping JSDoc support from documentation.js in the future, if I decide to make any large changes. I’d still support descriptions, examples, and other documentation that a program couldn’t infer. Unfortunately, the JSDoc style of comments has been forked so many times and specified so loosely that it’s a drag on project maintenance to the degree that it might be better to lose that audience and accomplish a feasible goal than to continue pushing the boulder uphill.

Which brings us back to the problem of forking: would that only weaken the JavaScript documentation environment?

I don’t quite know what to do about project maintenance: I think there’s a case to be made for dual-licensing the project and requiring commercial users to contribute either time or money. It’s frustrating, otherwise, to maintain a project that I don’t desperately need myself but is clearly benefiting companies that don’t actively contribute.