Some Node pitfalls – 1. Global state
I’ve been teaching myself Node over the last few weeks, by building a non-trivial application. In the process, I’ve been faced with a number of difficulties that I think are worth blogging about. First, let me say that I really like Node. It’s fast, powerful, smartly-designed, and has a liberating effect on my programming style. It is definitely trying to steer developers to a pit of success. It tends to do so, however, by surrounding the pit of failure with deep holes with spikes on the bottom…
Today’s post is about global state in Node. Global state is evil. It is, however, a little too easy to inadvertently create in Node. Node modules are loaded once, and then cached. That’s a good thing, of course, because reading files is expensive, and ‘require’ is a synchronous API. This is no different from a .NET application loading .NET assemblies, except that Node does it more lazily, and with typically smaller units of code (more good things). Because of that, however, anything that is defined on the module’s exports (what the module exposes and that the rest of the application can ‘require’), is essentially static, i.e. global state.
Take this foo.js module for example:
function Foo() {
}
module.exports = {foo: new Foo()};
This code will only be executed the first time the module is ‘require’d. Subsequent ‘require’s will just return the exports object as it was at the end of the first call. This means that require(‘foo’).foo is a singleton. Its state is going to be shared by the whole application.
That may look like an easy one to avoid, but I’ve only showed a trivial case of it. I’m currently investigating a bug in my code where the first request to the server works fine, but subsequent requests crash. I know that there is some hidden global state at play, but I haven’t found it yet. It seems to be hiding really well, and to be a lot more subtle than the above example.
The way I’ve been avoiding global state so far has been to manage my scopes very carefully, and by attaching the state that I need to an object that represents that scope unambiguously. The higher the scope, the more I try to keep state at a minimum, and immutable.
The site knows about the services that are available and enabled, and about its settings, that are immutable (or rather never muted).
Data that is scoped to the request is stored… on the request object, and this is passed around as a parameter when necessary. I’ve tried to keep that to a minimum, but I think I still have too much.
Services are either exposing static, stateless APIs, or they are providing an entirely new instance every time they are needed (they export the constructor, not an instance), and they encapsulate their state.
Did you have similar problems when learning node yourself? If so, how did you solve them?