A World Without Plugins

The case for Imperative Recipes

Developers have a complex relationship with plugins. We love making and using them - until things go wrong. Then they can ruin our day.

What happens if we did away with plugins altogether?

What are Plugins?

Plugins mostly take the form of encapsulated code. This code then takes a few lines of declarative configuration (sometimes point-and-click, which is great for non-technical users) to set up. The main platform exposes interfaces, like “hooks” and “lifecycle methods”, that then call the plugin’s code in (hopefully well documented and predictable) order. This saves the user from writing a lot of code themselves, which saves both the work to write that code and the knowledge needed to write it.

For developers, most plugin configuration looks pretty much the same:

// my random config file
const MyAwesomePlugin = require('my-awesome-plugin');
module.exports = {
  // etc
  plugins: [
    new MyAwesomePlugin({myPluginOptions: 'foobar'}),
    // etc
  ]
};

And we go back and forth on whether it should be static (JSON or YAML or TOML) or dynamic (JS or TS or Dhall or Prolog). (PS: here’s a poll I did to show you how opinionated people are about this!)

Most plugin systems also offer “local” plugins - plugins that you write yourself without having to be published to a plugin registry. Some platforms are “self hosting” - their own first party functionality also uses the same plugin system that they ask everyone else to use.

Some plugins offer a lot of value for you - they handle truly gnarly edge cases and save you thousands of lines of code. Others are merely thin wrappers over existing code, done for the sake of vanity or marketing or piggy backing on an existing popular ecosystem. The user would be better off just writing the code themselves.

Everybody Gets A Plugin!

Webpack has plugins. Babel has plugins, while also (confusingly) commonly being used as a plugin to Webpack. Meteor has plugins. Apollo Server has plugins. Next.js (may) have plugins. Gatsby has plugins that have plugins and plugins that have UI! Redux has middleware. Express has middleware. VSCode has extensions. Zsh has plugins. Vim has plugins.

I’m just naming stuff in my part of the world. I’m sure you could name more.

Why Everybody Loves (and Hates) Plugins

Every platform wants a plugin system. It helps people adjust the platform to their needs without forcing the core platform to develop and maintain that functionality. It helps third parties offer a faster onboarding to their tool with an existing popular platform.

Cynically speaking, it helps offload responsibility of the core platform to third party developers, while also making it stickier to users. More positively, it unleashes the creativity and resources of third party developers to scale the platform to usecases that that platform owners didn’t even think of.

A lot of professional app development starts with sticking together the right set of plugins to get desired features out of the box. If you know your tools well and the plugin ecosystem is mature, you can get up and running extremely fast.

At least, that’s the promise.

In practice, plugin quality varies widely, conflicts between plugins arise due to poor scoping, and having an easy resolution when things go wrong is rare. Unless you’re churning out the same app again and again, you’re always going to be running into and needing or writing some new plugin or other. Nobody is immune.

Btw, good luck if the platform decides it needs to make breaking changes to plugin APIs. You end up having to promise heaven (in features) or hell (in vulnerabilities) to end users in order to justify the development cost of the upgrade - it is not good enough to merely upgrade with no visible change on the surface. And because plugin authors are usually unpaid, the plugins are poorly maintained, and everyone is unhappy.

At large enough scale, even bugfixes are breaking changes. Hyrum’s Law says that “With a sufficient number of users of an API, it does not matter what you promise in the contract - all observable behaviors of your system will be depended on by somebody.” Even behavior you didn’t intend will eventually be relied on, and therefore you can’t break (eg. CSS).

Is the best developer experience just configuring plugin after plugin after plugin until we quit the industry to a life of underwater basket weaving? Is this the best humanity can do?

What would happen if we just didn’t have plugins?

If We Didn’t Have Plugins

If we didn’t have plugins:

  • The platform would be forced to support more things out of the box (eg Parcel)
  • The platform would be forced to document itself better, rather than rely on plugin authors and plugin users to find and support each other (eg Next.js vs Gatsby)
  • The platform would be forced to more heavily consider the exposed API surface area (eg React, Prettier)
  • More developers would learn how to use the platform directly instead of relying on probably poorer supported code and docs from plugin authors. This results in more transferable knowledge.

These all sound like good things. But what do we expect devs to use if we don’t have plugins? We can’t have everybody reinventing the wheel every time.

My Proposal: Recipes

I think the answer is Recipes. I think that plugins are a step too far on the “Declarative is always better than Imperative” mindset.

Recipes - to be clear - are like plugins in that you can look up a directory of what you want to do and copy and paste some code. They differ in that the code would be imperative instead of declarative. You no longer declare the name of the plugin and hope that it works - you copy and paste the actual code that will be executed.

Alt Text

This unlocks a few benefits:

  • Updating a Recipe is lower effort (a docs update) than Updating a Plugin (code publishing/signing/testing)
  • the developer learns the underlying system
  • the developer can use their knowledge of the language and associated tooling to debug.
  • the developer can modify the recipe for whatever weird use case they have without foisting that weight on the plugin author AND all other users.

And let’s face it - most of us have to look up docs to copy and paste declarative plugin config anyway. Recipes simply acknowledge that fact. In fact you’re more likely to not need to look up docs after copying a Recipe.

Possible Objections

But hold on, that’s only for the trivial plugins. Do you expect us to copy and paste thousands of lines of codes for the big plugins?

No. In fact that’s perhaps the biggest benefit of all - I expect the substantial code to be packaged up and distributed as libraries - independent and agnostic of the platform! This reduces the developer’s work back to copying simple recipes that use that substantial code.

If the plugin’s code is substantial, going beyond being a mere adaptor, it should be published as a standalone library. This library can then be reused beyond just the current platform. This benefits the library author, who doesn’t have to write for every platform, and benefits the developer, whose knowledge of the library is now transferable to the next platform (Every platform wants you to believe it is the last platform you’ll ever need).

If we denote the total number of platforms as M, and the total number of plugins/libraries (for each usecase) as N, then it seems evident that a world with plugins will require knowledge/maintenance of M * N platforms with their plugins, whereas a world with recipes will only require knowledge/maintenance of M + N platforms and libraries.

But hold on, there’s a reason we want declarative plugins - we can statically analyze them!

Yeah. I like static analysis too. But a) most plugin systems don’t really do much with it, and b) there’s nothing stopping Recipes from including some static data.

Sebastian Silbermann replies with another downside: If my plugin has a bug I can deploy a patch that everybody can install. If I have a bug in my recipe that bug is out there.

Conclusion

To be clear, all discussion in this essay only applies to developers. When it comes to nontechnical users, I agree there is clear value in writing plugins that people can click to install.

But for developers, I simply don’t agree that the value afforded by plugins is worth the cost of abstraction. We turn our brains off when we see declarative code - we just apply them as instructed and hope it works, and are at a total loss when it doesn’t. When the platform relies on us turning off our brains, that’s when we have lost.

I’ve been thinking about this ever since Andrew Clark commented:

All plugin systems are bad. I have never met a good one.

Imagine there’s no plugins. It’s easy if you try.

https://res.cloudinary.com/practicaldev/image/fetch/s--SvTJCo1M--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/bsfdn4r2gp1kzxu2yfz3.jpg

Further Reading

Tagged in: #tech #javascript #api design #dx

Leave a reaction if you liked this post! 🧡
Loading comments...
Webmentions
Loading...

Subscribe to the newsletter

Join >10,000 subscribers getting occasional updates on new posts and projects!

I also write an AI newsletter and a DevRel/DevTools newsletter.

Latest Posts

Search and see all content