Development

How to Tame Third-Party Packages with patch-package

Fix bugs or missing features in third-party packages with patch-package. Learn simple steps to apply patches and keep your codebase running smoothly without waiting for updates.

5 min
October 16, 2024
Dan Diemer
Mobile Lead

When developing software, we often face problems common to all developers, and we shouldn’t try to reinvent the wheel to solve them. To achieve this, we often reach for off-the-shelf packages that can help us overcome such problems.

Packages don’t solve all our needs

Occasionally, we’ll discover a package that can accomplish 95% of what we need it to do, but either because of a bug or a slight difference in intended functionality, it doesn’t fully satisfy our requirements.

What do we do in these situations!?

By the end of this article, you will be equipped with the knowledge to solve exactly that problem.

The Problem with Third-Party Packages

When presented with a package that has a slight deficiency, we have a handful of paths we can take to make it the perfect fit for our application.

Path 1 - Direct integration into your app

We can take the ideas presented in the package and directly integrate it into code in our application. Consequently, we’ve now moved the responsibility of the task from that package and added it directly to our application. The downside to this is that it can increase complexity and technical debt.

Path 2 - Forking the package and make changes

We can fork the package directly and make the changes we need. We’ll need to maintain this new package from here on out, which can cause additional overhead than we had when we were just maintaining our application. The downside is that if we publish it to a package repository, there might be confusion about why ours is slightly different.

Path 3 - Apply a patch after package installation

We can apply a patch right after the package installation that changes what we need. The patch will be applied only to the version installed, so we’ll need to check when upgrading to make sure our patch still works or is even still valid.

This article will focus entirely on path 3

→ Applying a patch to a package.

To do that, we’re going to use the appropriately named patch-package

Understanding patch-package

Patch-package describes itself as a “vital band-aid for those of us living on the bleeding edge”. At its core, it is a utility you can install with npm or yarn that allows you to apply changes to a package immediately after that package is installed. When inserted in to your development or build steps, this becomes a very powerful feature!

See the documentation for set up instructions.

Background

To understand how to use this tool, let’s dive in to a real world example!

On a past project we found ourselves in a challenging situation: we had just invested numerous days performing some needed upgrades for a variety of packages, including a major React Native version, and after resolving many dependency issues we discovered a bug with multiline TextInput. To make matters worse, all of this occurred right before we were supposed to cut a new release build!

We began scouring the issues list on the React Native GitHub repo, and discovered one that sounded very similar to what we were experiencing. The change proposed seemed like it could resolve the issue, but we had to decide the best way to implement it:

  • Use that PR’s branch as our dependency source for react-native
  • Wait days or weeks until it’s been merged
  • Apply the change as a patch

We opted to reach for patch-package to solve this issue for a few reasons: it’s succinct and understandable change and provides a timely solution to our current problem.

Using patch-package

Getting the fix in place was pretty straightforward. We followed these steps:

  1. Ensure that patch-package is installed by following the installation instructions on the package documentation.
  2. Make the appropriate changes to implement the fix.
    1. In our case this means editing node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
    2. We applied the fix as indicated, changingif (_ignoreNextTextInputCall) {To becomeif (_ignoreNextTextInputCall && [_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
  3. Run the npx patch-package react-native command to generate our patch

From there it was as simple as cutting a new build and testing that our fix worked. It did, we shipped it, and our client was happy!

What does a patch look like

Patches tend to be pretty small and easy to read thru, but depending on how complex your fix may be it can get a bit unwieldy.

In our case our patch is named react-native+0.71.8.patch  and looks like this:

react-native+0.71.8.patch screenshot example

If you looked at this and thought “That looks like a Git patch”, you’re right! patch-package makes use of git patches to apply your changes, which makes for a very portable and universal solution.

Because at it’s core it’s just a Git patch, you could simply apply it manually via git apply. However if you followed the installation instructions there’s a better way! By tapping in to the postinstall hook of the package manager, these patches can be applied automatically.

Managing your patch

Because the patch is name spaced to a specific version of the package you’re using, it’s easy to manage which patches are applicable to your current setup.

For our purposes, we will keep it in place for a period of time, but when we’re ready to upgrade our react-native package to one that supports our fix, we can just toss this patch away.

This is a huge advantage over having to use someone else’s package fork or maintain our own.

Important considerations

In our experience, a patch should be a temporary fix to a temporary problem. It’s intentional technical debt that we incur to pay off a problem. You have to make sure you keep a long term fix in mind, and move towards that solution when possible.

Additionally, there’s other great insights on the package documentation. Be sure to read it fully and understand exactly what is involved with applying a fix like this.

Conclusion

patch-package proves time and time again to be a valuable tool for managing and maintaining fixes to our codebases effectively. By allowing our developers to make targeted fixes and modifications to third-party packages without waiting for upstream updates, we empower teams to swiftly address critical issues and implement desired enhancements.

Our developers can confidently take control of our codebase's stability and customization, fostering more resilient and adaptable software solutions.

Actionable UX audit kit

  • Guide with Checklist
  • UX Audit Template for Figma
  • UX Audit Report Template for Figma
  • Walkthrough Video
By filling out this form you agree to receive our super helpful design newsletter and announcements from the Headway design crew.

Create better products in just 10 minutes per week

Learn how to launch and grow products with less chaos.

See what our crew shares inside our private slack channels to stay on top of industry trends.

By filling out this form you agree to receive a super helpful weekly newsletter and announcements from the Headway crew.