Ten Lessons Learned Maintaining Nodejs Modules
About the author
Node.js has grown so much since I started working with it sometimes I can't believe it. Not just the awesome community, but the core APIs.
Maintaining software is no walk in the park: writing modules is at once the most rewarding and most difficult thing you can do. I'm often reminded of what
isaacs once said on the nodejs mailing list:
It's like having children. Seems like a lot of pointless busy-work, and then you do it, probably without meaning or wanting to, and then suddenly fall so in love, you can't understand why everyone doesn't go as nuts over this little food-to-poop converter as you do. If you're not careful, you'll end up writing 3 or 4 more.
This article represents the lessons learned from writing node modules since
Maintaining Software is Hard
- Understand the Commitment
- Define What You're Going to do and do it
- Get Some Attention
- Keep your Dependencies Lean and Fresh
- Don't be Afraid to Refactor
- Be Wary of Compiled Modules
- Support Your Users
- Assess Pull-Requests Carefully
- Unpublish Versions you won't Support
- Know When to Throw in the Towel
Understand the Commitment
It bears repeating: maintaining software is hard. If you're not up to the challenge of responding to issues, publishing hotfixes, and generally making your module a priority in your life then you probably want to consider looking for something that already exists.
Define What You're Going to do and do it
By the time you've decided to commit to writing a module make sure you completely understand the problem at hand. Whether it's an API library, a utility module, or a framework be clear on the expectations early on. Too many times I've seen libraries that are doing too much or making choices that not all of their user-base will appreciate.
Get Some Attention
If you're new to Open Source, you need to understand that your modules don't just market themselves. You are defining a brand and you need to get some attention for it. More attention means more users. More users means better issues and more pull-requests. It's a positive feedback loop that is going to do more for your module than you could possibly imagine.
Keep your Dependencies Lean and Fresh
One of the toughest things about maintaining modules is maintaining your dependencies. When you're building an application you can shrinkwrap your dependencies to a hard version, but when you're building a module you need to be flexible. This means that your underlying dependencies can change without an explicit change from you.
Generally, setting a dependency version to
0.N.x (where N is a hard number) is safe, but be sure to stay on top of things or you're in for some unexpected surprises. It also means you should be careful what dependencies you take on. The more dependencies you don't maintain, the more unexpected complexity you may encounter.
Don't be Afraid to Refactor
There is nothing worse than coming back to a module after several months and realizing you made some bad initial decisions, or even worse your public API needs to change. Read up on Semantic Versioning and if you're module is pre-1.0.0, then don't be afraid to refactor. Refactoring can breathe new life into your module and your enthusiasm for it. Just be sure to update the version of your module appropriately and maintain a changelog.
In firstname.lastname@example.org, we refactored the core programmatic
Monitor functionality out into a new module: forever-monitor. This separation of concerns is important:
forever is a CLI application for running
forever-monitor with daemonization.
Be Wary of Compiled Dependencies
This is a subtle but brutally important lesson about writing Node.js modules. Taking on any compiled dependencies, whether as a node addon or 3rd party library, is going to increase the complexity of your install process by an order of magnitude. It is also going to (probably) make your module incompatible with your Windows users because they may not have a full compiler tool-chain.
In forever we learned this lesson the hard way. Until detached child processes were implemented in Node.js and libuv in 0.8.0 we were using a compiled add-on called daemon.node. At least 10% of all issues opened on forever (and a lot on npm itself) were related to this compiled dependency. Poorly configured Node.js installations, incompatible versions of Python (used by
node-gyp), and rebuilding between Node.js versions are just a small sample of the common errors your users will encounter if you go with compiled modules.
Support Your Users
When working with your users you have to divorce your ego. Be helpful, friendly, and responsive. Any attitude is poison to your success. Your first users will become your greatest champions in the Open Source community at large.
That being said, know when to say "no" and "not now". If your module gets successful you're going to get a lot of feature requests. It is not your responsibility to implement all of these features, nor to implement them immediately. Have a roadmap and stick to it.
Assess Pull-Requests Carefully
Once your module reaches a critical mass, you're going to get pull-requests. This is awesome. In the face of this awesomeness, however, don't lose sight of what's important. Make sure your own coding style, test coverage, and vision for your module do not suffer in your eagerness to merge a pull-request.
Unpublish Versions you won't Support
This lesson will probably draw some contention from the community at large. In fact, at Node Knockout last year, a team made an npm registry clone for ensuring this doesn't happen.
That being said, making this decision will help you explicitly manage the expectations of your users when they open issues on your module. By removing it from
npm you're saying implicitly that you will no longer maintain that version. If you've ever tried to keep multiple version branches in-line with hotfixes to master you understand how difficult this is and why it's not always worth it.
With forever, older versions were unpublished when we decided to no longer support the lowest Node.js version supported by that module.
Know When to Throw in the Towel
This is a sad, but true, fact of maintaining Open Source modules. If you don't have the time to keep your module going issues are going to pile up, feature requests will go unanswered and users are going to know. This immediately forces your users to fill the void and start publishing their versions to npm. Having multiple forks of your project being used is only going to fracture whatever community you've managed to build up.
All is not lost however. Many of the most successful Open Source projects are no longer maintained by their creator. When you realize that your module is falling by the wayside reach out to your most active users. Who opened the most pull-requests? Who is the most active on your mailing list? Who helps users with issues? Any user who matches one or two of those questions is likely your best candidate for a new maintainer.
A Grain of Salt
These lessons come from years of experience writing and maintaining Node.js modules, but it is important to remember these experiences are my own. You may decided to do things differently and that is a good thing. Experiment, be creative and keep hacking!