How do you gradually remove singletons from legacy code and move to DI?
The gist
Singletons in legacy code cannot be removed in one refactoring: X.shared spreads across hundreds of files, and behavior depends on an implicit initialization order. The approach that works is the strangler: small steps, after each one the code compiles, behavior does not change and you can ship. First the singleton hides behind a protocol, then it is injected through a default parameter, then creation moves up to the composition root, and only at the end does shared disappear.
The rest of this answer, with code and interview follow-ups, is in the DevPrep app.
Read the full answer in DevPrep