Refactoring, in its purest form, is the improvement of the underlying workings of an application (or cohesiveness of its structure) without negatively affecting the end user experience. It involves making an application more maintainable and robust without having a detrimental effect on the application functionality. Put simply, traditional refactoring should be sleuthy – the user of the application should not notice any difference in the program before and after the refactor.
I find that, sometimes, when I attempt to refactor something in this way I hit a brick wall, because the program I started with was so broken – so tightly coupled, copied and pasted, and untested – that it simply isn’t possible to refactor without overlooking some of the smaller details in the user experience.
I’ll give you an example. One of the WordPress sites I’ve been given the task of managing has well over 30 template files tied to specific WordPress categories, e.g. single-32.php, single-35.php, and so on. They all have much the same code and markup, apart from a different category ID which is passed into the query string to retrieve the latest posts from that category. Changing the appearance of the site’s category pages requires making the same tweaks to a great number of files.
When I started refactoring the site, I created a generic single.php with category handling logic to remove the need to have all of these separate template files. But then I noticed that a few of the templates had subtle differences – an extra div here, a missing classname there, and so on. Conditionally incorporating these specifics into my generic template would be confusing, messy, and would do nothing to mitigate the original problem.
It’s possible that these subtle differences were the result of configuration drift; the requirement to apply updates to so many template files could no doubt lead to human errors where updates are applied incorrectly, or not at all. But it’s also just possible that all of these little design differences were in fact intentional, and by discarding them I’d be having a negative and noticeable impact on the user experience.
I’d like to quote an extract of The Zen of Python, by Tim Peters.
Special cases aren’t special enough to break the rules.
By attempting to get all of the category pages to adhere to the rules of one common template, I’d be breaking the special cases. But good programming practice suggests that we should be more concerned that we might be breaking the rules to accommodate the special cases.
So I had a dilemma: do I continue with my refactoring step? It would significantly improve the site’s maintainability, and would cut the development time of future updates to a fraction of what they would otherwise be. On the other hand, going ahead would mean cutting out the intricate details of a minority of category pages.
I decided to continue. But by doing so, I realised I wasn’t refactoring in the traditional sense of the word, as the user experience wasn’t being left untouched. What I was doing instead was something I like to call orthodontic refactoring.
Sometimes, you have to break things before you can improve them. Take, for example, a teenager with slightly wonky teeth. Their incisors are misaligned, they don’t feel comfortable smiling. What happens? For a short while, an orthodontist will make them look worse by giving them braces: not only do they have a wonky smile, but their mouth is now packed to the rafters with metal scaffolding.
In a few months time, the braces come off and they can smile confidently for the first time. The orthodontist made things worse for a short while, but in the end it all paid off.
Where possible, you should still attempt to refactor without breaking application functionality. But if going from 100% functionality to 97% significantly simplifies, optimises and further protects the system, can’t we worry about reintroducing the other 3% later?