This last few weeks, I’ve been refactoring much of our legacy codebase; some components of it were in desperate need of replacing entirely, since they were no longer fit for purpose.
One of these components was a class we called MysqlX – a class that handled much of the website’s database communication. This was a crucial component to refactor; the idea was to remove dependence on the deprecated mysql_ functions and shift over to the Laravel’s Capsule – a component which is already extensively used across the newer portions of the system.
We considered a number of approaches; below are some of the constraints we were working under:
- The MysqlX class is initialised a single time at the bootstrap stage.
- global variable $mysqlx is utlised no less than 800 times throughout the codebase.
- As with all projects, we are working until a time/resourcing constraint.
Obviously, with all this in mind, writing a decorator class, or replacing the class entirely, applying to all instances individually, was inconceivable. We had not the time, nor the in-depth knowledge of each use, to safely replace all 800 instances.
So, I came up with a method of refactoring that was deemed safer, and far quicker. This was to be a temporary refactoring to quickly improve the performance of the architecture, with minimal resource overhead.
I call this pattern, the Impostor Pattern.
At present, our entire site architecture spoke directly to the MysqlX instance that was initialised at bootstrap level. This gave us a single point of initialisation; an important requirement of the Impostor pattern.
The simple description of this architecture is that instead of talking directly to the class you wish to refactor (the deprecated class), you can instead speak with an impostor class. This impostor class will route requests through to the deprecated class using PHP’s built-in magic methods, or via subclassing. Over time, you will replace each of these methods in the impostor class with newly refactored or reworked code. Eventually, once you have refactored each of the methods, your impostor will assume the role of your deprecated class.
There are several advantages to this. Firstly, you are able to refactor, without modifying the code in-situ, where the deprecated class is being utilised. Secondly, it’s a quick method of refactoring, allowing you to safely remove reliance upon a deprecated class.
Note: this method will not apply to all circumstances; especially those where the deprecated class is easily accessible within the codebase. There are much more effective strategies for this, such as using a decorator class.
There is a few key differences between the Impostor Pattern and the Decorator Pattern. The Impostor Pattern requires no modification of the in-situ codebase. The Impostor Pattern requires no dependence on the class to be deprecated.
Stages of Development
The learning stage occurs when the impostor has been implemented, but there are no refactored methods present within it. At this stage, you are provided with a unique opportunity to use logging or metrics to really learn about how your deprecated classes are used. This stage can be released onto production, since it’s relatively harmless.
This stage occurs when you’re prepared to refactor a method of the deprecated class. You are to recreate this method on the impostor, which removes the reliance upon the original deprecated version.
The edge cases stage is reached when you feel you’ve taken care of all the refactoring required by the project. Now is a time to look at what you’ve learned in the learning stage, to identify any edge cases for which you’ve not catered for.
The retiring stage is when the deprecated class is finally ready to be removed.
- Create a class (the impostor) that sits between your code architecture and your class to be deprecated
- Utilise magic methods or subclassing to ensure that all requests are routed through to the original.
- Add logging or metrics to learn about how your deprecated classes are used.
- Create and refactor each of the deprecated methods on your impostor class. Testing at each juncture.
- Take care of any missed edge-cases.
- Retire the deprecated class.