Let's say someone has implemented a module for sets that's being used in two modules.
a.js:
var set = require("set");
export.thing = set(1, 2, 3);
b.js:
var set = require("set");
export.thing = set(4, 5, 6);
You're using both:
var thing_a = require("a").thing
, thing_b = require("b").thing
Imagine that b
depends on a newer version of set
which has a completely new (faster!) internal structure. This structure uses a new (public) API for computing intersects/unions between sets. This means that's it's impossible to use thing_a
and thing_b
together:
thing_a.intersect(thing_b) // => Err!
thing_a.union(thing_b)) // => Err!
It's easy to to incidentally creates these sort of dependencies inside your app. If a
and b
did use the same version of set
, you might have used the code above. By upgrading either a
or b
there's a chance that your code might mysteriously break.
More concretely:
Every object that comes from
module O, version A
throughmodule A
can only be safely handled by amodule X
if (and only if)module X
also has a dependency onmodule O, version A
. (Byversion A
I don't necessarily mean the exact same version, but the same version of the public API).
This turns out to be the exact same constraint as single-namespace (Ruby, Python, Perl etc.) language imposes.
Conclusion:
For modules that are only used internally in a module and never used across modules: NPM trumps. There's no way your dependencies can interact with other modules.
For modules that are used across several modules: You need to (globally) use the same module. Technically, you don't have to, but it's going to be a lot of pain and horrible for encapsulation.
This does make some sense -- the are concepts that appear common enough that you think it's okay to emit instances of them from your APIs. In theory a library should not allow objects other than those in the language/stdlib or that it created itself to pass through its interface, but I doubt I stick to that.
In Node, this seems to be translating into people using uniform interfaces, e.g. programming with streams, always yielding an error as the first callback argument, etc.