Inventory where the library is used, pin its behavior with tests, put a seam in front of it, migrate in small slices, then delete it and confirm it no longer appears in any build. Each step is reversible until the last one, which keeps the risk low.
When you need this
The trigger might be an archived repository, an advisory that never gets fixed, a license change, or a feature the library cannot deliver. This is the execution step of the exit plan you hopefully wrote at adoption. If you did not, you will build it now.
Step 1: inventory every use
Search for imports across all repositories, not just the one you are in. Include build files, scripts, containers and documentation examples. Note which services ship it and which only use it in tests. An accurate software inventory or SBOM makes this faster than grep alone.
Step 2: write characterization tests
Before changing anything, write tests that capture what the library does for your real inputs, including odd ones. These tests describe current behavior and become the acceptance test for the replacement.
Step 3: add a seam
Create a small interface of your own and route calls through it, as described in wrapping a library. Ship this step alone. It changes no behavior and is easy to revert.
Step 4: build the replacement
Implement the interface with the new library or in-house code. Run the characterization tests against both implementations. Differences will appear in edge cases such as encoding, empty values and error types. Decide for each whether to match the old behavior or fix it.
Step 5: migrate in slices
A feature flag or configuration switch lets you move one service or code path at a time.
| Slice | Action | Rollback |
|---|---|---|
| Tests and tooling | Switch first | Revert flag |
| Low traffic service | Switch next, watch logs | Revert flag |
| Core paths | Switch last | Revert flag |
Step 6: delete and verify
After a stable period, remove the old implementation and the dependency from manifests and lockfiles. Then verify:
- The package is gone from every lockfile and container image.
- Your scanner no longer reports it.
- No code, documentation or configuration still mentions it.
- Any transitive packages only it needed also disappear.
If you published the library internally, consider a deprecation notice for downstream teams. For packages you control on npm, the registry's deprecate command gives a warning to anyone who installs the affected versions.
Try it yourself
Click through it at your own pace, or open it full screen. Use it to picture the inventory step: which components exist and where.
Where Safeguard fits
Safeguard's SBOM and dependency scanning help with the inventory and the final verification. Autonomous remediation with fix PRs is available but off by default, with every category set to Manual. Try the free scan at gold.safeguard.sh/scan.
Quick check
Can we just delete it and fix what breaks?
That works for a leaf utility with tests. For anything widely used, the seam and slice approach is safer.
How do we know it is really gone?
Check lockfiles, built images and a fresh scan. Cached layers and forgotten services are the usual leftovers.
Sources
Last reviewed: 19 October 2026.