Problem statement: migrating to a new library
For one reason or another, library A has been deprecated and you are tasked to migrate to library B.
The problem is that library A is spread all around your production and test codebases with direct calls to its classes without any interface in between. This poses the following challenges:
- Since library A is used directly in code, it is called an implicit dependency, which obfuscates how it is being used making it harder to migrate.
- If library A is used 100 times, and you happen to misuse its equivalent in library B, then you would need to go back and change all of them.
- What if two years from now you now need to migrate to library C?
- You may also want to swap between A and B using a feature toggle which becomes a hassle if library A is used 100 times. You can read more about feature toggles here: Don't break production when you add new features! - Feature Toggles in C# @ dev.to
Surely there is a better way to attack this problem.
Solution: implement the adapter pattern before migrating
The adapter pattern
Adapter is a structural design pattern that allows objects with different interfaces to collaborate. An adapter is a special object that converts the interface of one object so that another object can understand it. [1]

Adapter pattern image from refactoring.guru
Using the adapter pattern can help in our task by:
- Since we will be refactoring to dependency injection we are making the dependency on the library explicit, making it easier to understand where it is being used.
- We can swap between both libraries easily in our composition root.
- If we happen to add a bug while implementing the new library, we can just fix the adapter implementation instead of the hundred implicit calls in the codebase.
- If you need to migrate again to a new library, we can just implement the adapter for it. We only need to refactor to an adapter once!
Migrating to a new library
We will follow 4 straight forward steps:
- Implement the adapter pattern for the existing library.
- Change all dependencies of the existing library to the created adapter interface using dependency injection.
- Implement the same adapter interface for the new library.
- Inject the new library instead of the old library.
Example: from Newtonsoft.Json to System.Text.Json
Disclaimer: this is a purely informative/basic example. You are probably dealing with more complex situations.
You are currently using NewtonSoft.Json in your codebase and you want to migrate to System.Text.Json. You also want to avoid the already discussed problems, so you decide to implement an adapter first.
Following the steps above we do the following:
Create an adapter interface called
IJsonAdapterin which we declare common methods such asSerializeandDeserialize. We then createNewtonsoftJsonAdapterwhere we implement theIJsonAdapterinterface.

In our codebase we then inject the
IJsonAdapterinterface and use that instead ofNewtonSoft.Json.

Finally, we inject
SystemTextJsonAdapterinstead ofNewtonsoftJsonAdapter. Optional: we can use a feature toggle to swap between easily.

References
[1] Adapter. Refactoring.Guru. (n.d.). https://refactoring.guru/design-patterns/adapter


Top comments (0)