February 3, 2015

The philosophy behind our plugin ecosystem

On my daily trawl through the blogosphere, I often find reflections from other startup founders that turn out to be very useful in working with NodeBB. Today I read the reflection by Craig Morrison’s article on 8 Steps to Building Features Your Users Actually Want.

In a nutshell, he eschews the building out of every single feature request in favour of building out only those that make sense within the "grand vision" of a product.

At my last startup, we got bombarded with feature requests. The founder was very gung-ho about including them all.

It was eventually the downfall of it, in my opinion.

— Craig Morrison, UsabilityHour

This isn’t the first time I’ve read an article with that exact sentiment. How many open source projects have you seen where the developers have given up and let the project stagnate?

In our haste as startup devs, we often are blinded by the fact that building something isn’t always better than building something useful. Cranking out features is often de rigueur when you’ve got caffeine in your veins, and the sky’s the limit, but seldom do we think about the plain and simple adage:

Quality over quantity

Of course, I’m not saying that we should meticulously plan out everything about the product from the get-go. Plans change, ideas pivot, and while hindsight is 20/20, foresight is often completely blind1.

There are three simple criteriae for deciding whether a feature makes it into core, or whether we cast it away into plugin-land:

1. Is it useful?

Very few features make it into core unless it’s something that a clear majority of users have expressed an interest in. This isn’t really an exact science, but our method of discussing the merits of a feature in an RFC2 have led to spirited debate, and have even caused a decision for inclusion to be reversed outright.

Usefulness is tricky, mostly because it is different for everybody! One man’s trash is another man’s treasure, etc. However, this comes in handy as it makes it very easy to decide that certain features are superfluous.

NodeBB’s threshold for "usefulness" is very high. In almost all cases, we’ll want a feature to be maintained in the form of a plugin. This allows us to keep the core code lean, and the more plugins get made, the more deficiencies can be found in the plugin system, that when resolved, allow plugins to really extend NodeBB.

After all, lots of people (@psychobunny included) would love a plugin that pinged an API, returned the latest scores in a world cup match, and displayed it as a widget, but somebody maintaining a NodeBB for an internal Fortune 500 company might not find it as useful.

2. Are we okay with maintaining it?

Maintenance is a pain. I don’t have fancy statistics, but every single line of code added increases NodeBB’s complexity. Every single method and every single feature introduce yet another moving part in a substantial machine, and it’s not a surprise that the more complex a piece of software, the more ridiculous edge cases you can find.3

At NodeBB, we try fairly hard to code in ways that reduce side effects. We stay away from storing state in globals, modularise as many things as possible, and of course, we push for additional functionality in the form of plugins.

It’s not simply a matter of "if we add 200 lines of code, that’s another 200 lines to maintain"! We really take "less is more" to heart.

Will this cause unnecessary lock-in?

The most recently added criteria we consider is whether it causes lock-in. All members of the founding team made huge decisions at the beginning of NodeBB that shaped its future. Some have paid off4, while others cause frustrations to this day.

The best (two) examples I can think of are our decision to use Bootstrap as a CSS framework, and our hand-rolled post composer:

Our posts composer

The former started to become a problem when people wanted to replace Bootstrap with a different framework, like Foundation, or Pure CSS. Our theming system does allow another framework to be used, but so much of our client-side code happened to expect Bootstrap (binding events to bootstrap-only classes, for example), that it was nearly impossible to swap it out for another.

The latter had always been an issue as the default composer is admittedy a little bland. I had made the following sweeping assumptions:

  1. That "there were no good editors", and
  2. That markdown was going to be the markup to end all markups.

Now granted, we support other parsers besides Markdown, but the composer itself inputs markdown syntax, which would admittedly be confusing if your board happened to use BBCode 🙂

Some Downsides

I would be remiss if I didn’t point out that maintaining a plugin ecosystem came with its share of challenges.

  1. Maintaining a package manager as a superset of npm was a good move, but there are calls to upgrade the package manager to do more than be a dumb list of NodeBB plugins.5
  2. The majority of people who build plugins tend to build them for their own NodeBB, and thus they have pretty poor documentation. We’ve yet to discover a remedy for this.
  3. Poor plugin developers get shafted every time we change the underlying plugin system.
  4. Small, modular plugins can be abandoned just as easily as large, complex projects. One could argue that the relative size is actually an incentive in abandoning a plugin!


  1. I take great pride in the fact that NodeBB’s first plugin is a plugin that parses all post text and changes instances of "the cloud" to "my butt". Hell, this isn’t blind foresight, everybody needs this plugin.
  2. "Request for Comments". Did you know you can check out the curent RFC’s here?
  3. At my first gig, we made the admin panel a single page app before single page apps were even considered a thing. One of my pages would only load if you happened to load a separate (completely different) page first. I distinctly remember giving up on figuring out why.
  4. Mobile-first design was really a good idea, in the end.
  5. Did you know there’s an RFC for this?
© 2014 – 2023 NodeBB, Inc. — Made in Canada.