Let's say you have a static library like this:
class SessionHelper
{
static public void Foo()
{
//Implementation
}
static public void Bar()
{
//Implementation
}
}
And a program that uses it, that you'd rather not monkey with too much:
class MassiveSpaghettiCode
{
void DoSomethingNobodyUnderstands()
{
SessionHelper.Foo();
SessionHelper.Bar();
}
}
I would extract the static methods to an interface and copy the implementation code from the static class, only exposing each method as an interface member.
interface ISessionHelper
{
void Foo();
void Bar();
}
class SessionHelper : ISessionHelper
{
public void Foo()
{
//Implementation
}
public void Bar()
{
//Implementation
}
}
Then, expose an instance of the implementation via a member property with the same name as the original static class. This allows the legacy spaghetti code to continue to function without any modification. For bonus points, we can inject the instance if we want.
class MassiveSpaghettiCode
{
protected readonly ISessionHelper _sessionHelper;
protected ISessionHelper SessionHelper
{
get
{
return _sessionHelper;
}
}
public MassiveSpaghettiCode() : this( new SessionHelper() ) //Default constructor requires no injection
{
}
public MassiveSpaghettiCode(ISessionHelper sessionHelper)
{
_sessionHelper = sessionHelper; //Injected, or injectable at least
}
void DoSomethingNobodyUnderstands()
{
SessionHelper.Foo(); //Notice this code hasn't changed at all
SessionHelper.Bar();
}
}
Once you have the existing code extracted to an interface/implementation, write a series of integration tests and get it nice and stable. You want to make sure you understand what it means to fulfill the contract implied by that interface,not just syntactically but semantically.
Once you have a thorough understanding of the interface as implemented by the legacy code, create a new class, implementing the same interface, but with brand new code using your newer back end technology (Redis, or whatever). Run this class through exactly the same unit tests. Be sure you can explain any difference in test outcomes; ideally there shouldn't be any.
Once all of the tests pass identically, you're ready to switch it out, which is as easy as changing the one dependency.
P.S. I believe this is pretty much the approach Microsoft took when migrating everyone from classic ASP to ASP.NET, e.g. the Session and Response syntax looked the same but worked rather differently.