February 2023
3 minute read time
A quick introduction to phantom dependencies
Martin Torp
By Martin Torp
Cofounder of Coana
PhD in Computer Science

<m>A common [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) in npm package management is the use of phantom dependencies.
You can avoid phantom dependencies by using the npm alternative [pnpm](https://pnpm.io/), but let me get back to that in a minute.</m>

<m>Phantom dependencies are dependencies you use but don't explicitly include in your package.json.
If you depend on the package [firebase](https://www.npmjs.com/package/firebase) you can write `require('@firebase/app')` even though [@firebase/app](https://www.npmjs.com/package/@firebase/app) is a different package from firebase.
It works because @firebase/app is a dependency of firebase and npm by-default moves dependencies to the outermost node_modules folder (a process called hoisting).
If you install firebase, you will find that @firebase/app has been hoisted to the path `'node_modules/@firebase/app'` instead of `node_modules/firebase/node_modules/@firebase/app'`.</m>

<m>So let's consider why phantom dependencies are a problem.
Imagine you depend on the [parse-git](https://npmjs.com/package/parse-git) package.
parse-git depends on [lodash](https://npmjs.com/package/parse-git), so since lodash is hoisted to the outermost node_modules, you can use lodash in your code without explicitly including it in your package.json.
The problem arises the day the authors of parse-git decide that they no longer need lodash and remove the dependency ✂️
Now your code is going crash with a module not found error 💣
Even worse, if lodash is updated to a version with breaking changes, then your app may not crash, but suddenly start to misbehave in a difficult to debug manner.</m>

<m>So now we know why phantom dependencies are bad.
As a rule-of-tumb: if you write `require('npm_pkg')` or `import ... from 'npm_pkg'` make sure npm_pkg is in your package.json.
You can also try [pnpm](https://pnpm.io/), which explicitly disallows phantom dependencies. You will get a module not found error if you try to use a phantom dependency with pnpm.</m>

<m>But what about the firebase example from before?
The [firebase documentation](https://github.com/firebase/firebase-js-sdk/tree/master/packages/app) explicitly says that '@firebase/app' should be used as a phantom dependency 😮
Since the authors of firebase are also the authors of @firebase/app, we can expect that they are careful not to break @firebase/app in an update of firebase without being very explicitly about it in the changelog.
But since pnpm doesn't allow phantom dependencies, does that mean we cannot use firebase in a project that uses pnpm as a package manager? 🤔
Not quite!
You can tell pnpm that you want to be able to use the phantom dependencies of firebase', but you have to be explicit about it
Just Include:</m>

<m>```
public-hoist-pattern[]=*firebase*
```</m>

<m>In your .npmrc file in the root of your project.</m>

Questions or opinions?

Feel free to reach out to us by email or through our Slack Community anytime. We'd love to hear from you.

Join waitlist

Sign up to get notified about future Coana future launches.