Yes. Put an abstraction in front of the old library, add the new library behind the same interface, and use a feature flag to choose which one serves each request or to run both and compare results. This lets you swap a risky dependency in small steps, with an instant switch back.
The pattern in plain terms
Martin Fowler describes branch by abstraction as a way to make a large change gradually while still releasing regularly. You create an abstraction layer that captures how a part of your code talks to the current supplier, move callers to use it, build the new supplier behind the same layer, then switch over and delete the old one. He notes that feature flags can be used to run the new supplier in test environments and compare its behavior with the old one.
Fowler's article on feature toggles separates flag types by purpose and lifetime. A flag that gates a migration is a release toggle, which is meant to be short lived.
When this is worth doing
| Situation | Use this pattern? |
|---|---|
| Replacing an abandoned or vulnerable library with a different one | Yes |
| Swapping a parser, crypto helper or HTTP client | Yes, with comparison |
| A routine minor upgrade of the same library | No, use normal tests |
| Two versions of the same library cannot coexist | Hard, consider other methods |
Steps
- Define the interface. List the few operations your code needs from the library. Keep it narrow.
- Wrap the old library. Implement the interface with the current library. Move callers over. Ship that with no behavior change.
- Implement the new one. Write the second implementation against the same interface and test it directly with the same inputs.
- Add the flag. Default off. Scope it by environment first, then by percentage or tenant.
- Shadow compare (optional). For a pure function such as a parser, call both, return the old result, and log differences without affecting users. Mind the cost and never log sensitive values.
- Ramp. Move traffic in stages, watching errors and latency per implementation.
- Remove. When the new path is fully on and stable, delete the old implementation, the flag and the abstraction if it adds nothing.
Risks to manage
- Two code paths, two sets of bugs. Test both while the flag exists.
- Flag debt. A migration flag left in place becomes permanent complexity. Give it an owner and a removal date. See feature flag debt expiry dates.
- Flag outage behavior. Decide what happens when the flag service is unreachable. See what flags should default to.
- Side effects. Do not shadow call anything that writes data or contacts an external system.
- Security review. The new library brings its own supply chain risk. Review it before the ramp.
Where it fits in an upgrade program
This is a tool for replacing a dependency, not for every bump. Use it when a vulnerable library has no fixed version or when a major rewrite is needed. For the rest, normal tests and a canary are lighter. See how long a canary should bake.
Safeguard can show which components in your inventory are vulnerable or unmaintained, which helps decide where a swap is justified. Free scan: gold.safeguard.sh/scan.
Quick check
Is a feature flag enough to roll back a bad library swap?
Yes for the code path, provided the old implementation is still deployed. It does not undo data written by the new path, so avoid side effects during the ramp.
How long should the flag live?
As short as you can manage. Set a removal date when you create it and delete both the flag and the old code after the ramp completes.
Sources
Last reviewed: 19 October 2026.