Do not start by rewriting. First check whether you really need the package, whether a newer or older version has a different license, and whether an alternative exists. Wrap the old dependency behind a small interface, swap the implementation, and record the decision with a date.
Start with whether the block is right
Before you spend a sprint, confirm the finding. Check the declared license at the exact version in your lockfile, not at the latest version. Check whether the package is dual licensed, which can let you pick a permitted option. See dual licensed packages for how to record that. Check how the package reaches your product: build only, development only, or shipped. Our post on build time or shipped explains why that changes the answer.
If the block holds, pick one of five routes.
| Route | When it fits |
|---|---|
| Different version | An earlier or later release carries an acceptable license |
| Exception | The use is internal, or the risk is accepted with an expiry |
| Alternative package | A comparable library exists with an allowed license |
| Standard library or existing code | You use a small part of the package |
| Write it yourself | The need is narrow and the logic is simple |
This is engineering guidance, not legal advice. Exceptions should go through your normal approval path.
Steps for a swap
- Find every use. Search the codebase for imports of the package, not just the manifest. Count the call sites and the distinct functions used. The count usually surprises people, in the good direction.
- Add a seam. Create a thin internal module that exposes only what you use. Point every call site at it. This step changes no behavior and can ship on its own.
- Pick the replacement. Check its license, its maintenance, and its own dependencies. A replacement that pulls in a denied transitive license solves nothing.
- Build the new implementation behind the seam. Keep the old one in place.
- Test behavior with the same inputs on both. Pay attention to edge cases such as encoding, time zones, errors and ordering.
- Switch the seam, remove the old package from the manifest, and regenerate the lockfile.
- Re-scan to confirm the denied license is gone from the inventory, including transitive entries.
Record what you did
A short entry in your decision log: package, version, license, why denied, route chosen, replacement, date, approver. A year later someone will ask why the team did not use the popular library, and the entry answers it. The fields in the license decision log post work well.
Prevent the next one
Most of these replacements happen because the license check ran late. If the package arrived through a pull request, the gate would have blocked it before it spread across 40 files. Run the license check on the pull request that adds the dependency, and block there. See turning on a license gate without breaking every build.
Try it yourself
Click through it at your own pace, or open it full screen. Watch where in the flow the block happens, since earlier is cheaper.
Safeguard's SBOM and dependency scanning builds a component inventory with license data, and policies and gates can block a pull request on a license rule you define. You can try a free scan at gold.safeguard.sh.
Quick check
How long should an exception last?
Give every exception an expiry date and an owner. Shorter is better, since the review cost is small compared with a forgotten risk.
Is a fork of the denied package a way out?
A fork keeps the original license, so it rarely solves a license problem. It adds a maintenance job on top.
Last reviewed: 19 October 2026.