How I Fork and Publish NPM Packages to Fix Bugs (And Why You Might Not Want To) | Chris Morrison

How I Fork and Publish NPM Packages to Fix Bugs (And Why You Might Not Want To)

Recently, I found myself in a familiar situation: I discovered a bug in an npm package I was using, and I needed to fix it quickly for my project. While waiting for the maintainers to address the issue wasn't an option for my timeline, I also knew that forking and publishing my own version of the package could introduce problems.

After this issue happened a few times, I decided that I wanted to address the issue. I'll walk you through my process for forking and publishing packages (when you want to fix a bug in a package), and I'll share some important considerations that might make you consider your options before taking this approach.

Important note: This is something I rarely recommend, but in specific situations, it has been the best option for me. Use at your own risk, because often, this is not the best option. I'll discuss when to use this and when not to use this.

Why Fork and Publish?

Sometimes you need an immediate fix. Maybe you've found a critical bug that's blocking your deployment, or perhaps you've discovered an edge case that affects your specific use case. In these situations, forking and publishing your own version can be a quick solution that gets you unblocked and back to building.

The first time I wanted to do this, I found an edge case bug that only affected me. The maintainers wouldn't fix the bug since it would break everyone else's code, which is completely understandable and the right move on their part. So, instead of copying and pasting their code into my project, creating my own package seemed like the best option.

My Step-by-Step Process

Here's how I handle forking and publishing an npm package when I need a quick fix:

1. First, I fork the repository on GitHub. I make sure to name it clearly, usually keeping the original name with my username as a prefix. This helps me track my forks and makes it clear to others that this is a temporary fix.

2. Then I clone my fork locally:

git clone https://github.com/myusername/package-name
cd package-name

3. Before making any changes, I create a new branch:

git checkout -b fix/describe-the-bug

4. After making my fixes, I update the package version in package.json. I usually add a suffix to make it clear this is my fork:

{
  "name": "@myusername/package-name",
  "version": "1.2.3-myfix.1"
}

5. Next, I make sure I have an npm account and log in:

npm login

6. Finally, I publish the package:

npm publish --access public

7. In my original project's package.json, I update the dependency to use my forked dependency:

{
  "dependencies": {
    "@myusername/package-name": "1.2.3-myfix.1"
  }
}

The Good Parts

This approach has several benefits that I've come to appreciate:

  • Immediate Relief: You get unblocked right away and can continue with your project.
  • Full Control: You have complete control over the fix and can tailor it to your specific needs.
  • Learning Opportunity: Working with package internals helps you understand the ecosystem better.
  • Contribution Potential: Your fix can serve as a basis for a pull request to the original repository, if you're change fixes a known bug.

The Not-So-Good Parts

However, there are significant drawbacks that I've learned to consider carefully:

Maintenance Burden

Every forked package is technical debt. You're now responsible for keeping your fork up to date with the original package, or you risk missing important updates and security fixes. I've been burned by this more than once, finding myself stuck on an old version because updating would mean re-applying my fixes to the new version.

Version Conflicts

If other packages in your project depend on the original package, you might run into version conflicts. npm's dependency resolution isn't always smart enough to handle forked packages gracefully, especially if multiple packages require different versions of the original.

Security

When you fork a package, you're also taking on the responsibility for security updates. If the original package gets a security patch, you'll need to update your fork manually. This can be particularly problematic in larger organizations where security audits are common.

Long-term Viability

What starts as a quick fix can turn into a long-term commitment. I've seen temporary forks become permanent parts of codebases because teams forgot about them or didn't have time to migrate back to the original package when it was fixed.

Better Alternatives I've Found

Before you fork, consider these alternatives that I've found helpful:

  1. patch-package: This tool lets you make changes to your node_modules directly and creates patches that can be version controlled and automatically applied during installation.
  2. Override Dependencies: Many package managers now support overrides, allowing you to force specific versions of nested dependencies without forking.
  3. Wait it Out: Sometimes, waiting for the maintainers to fix the issue is less work overall than maintaining a fork.

When I Do Fork (And When I Don't)

I now only fork packages when:

  • The bug is critical and blocking my work
  • I've already opened an issue and provided a clear reproduction
  • The maintainers are unresponsive or the fix timeline doesn't align with my needs
  • I'm confident I can maintain the fork until the upstream fix is available

I avoid forking when:

  • The bug is minor or has workarounds
  • The package is complex with frequent updates
  • The fix might introduce security implications
  • I don't fully understand the package's internals