Category: Programming

  • Orthodontic Refactoring

    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?

  • The pessimistic ultimatum

    When developers do their work, they should constantly bear in mind two scenarios.

    What if my computer blows up?

    What if I blow up?

    These two questions encompass what I like to call ‘the pessimistic ultimatum’, which questions what might happen should the worst happen.

    What if my computer blows up?

    Let’s consider the first scenario. Though it’s unlikely your computer will ‘blow up’, there’s always the chance your hard drive will fail, or your laptop will get stolen, or you accidentally run some dangerous rm -rf * command.

    You’ve been coding solidly for five hours, and have been saving locally every few minutes, when suddenly your laptop dies. What do you do now? Is there any way of getting your hard work back?

    One should always bear in mind the possibility of hardware failure, and ensure they have at least a moderately up-to-date version of their work copied somewhere not on the local machine.

    GitHub, or some other source control system, is probably the best solution. Don’t be afraid to push your changes on a fairly regular basis; at least a few times a day is good practice.

    An automated backup system is a good alternative if you can’t be bothered with Git. Keep your project inside something like Dropbox, where all changes are backed up in the cloud and files can be pulled down remotely from any computer, provided you remember your Dropbox username and password.

    At the very, very least, copy your work to another device, be it a portable hard drive, USB stick, or another computer. However, these backups are subject to the same risks as the local computer, and I even know of someone whose computer crashed halfway through copying to a USB drive, rendering both copies useless.

    What if I blow up?

    Though your own spontaneous combustion is even less likely than that of your computer, you can never guarantee your life won’t be cut short by an unexpected bus careering around the corner, or a knife-wielding murderer, or a slip onto the cold, unforgiving pavement.

    It doesn’t necessarily need to be your own demise that gets in the way of work- you can be taken ill, or forget the password to your account, or simply forget to document your work before switching jobs.

    Backing up your work to multiple locations is half of the problem. The other half is sharing your knowledge with others so that your work can actually be put to good use.

    Let’s go back to that code you’ve been working on for five solid hours. Stop and think to yourself: ‘if I pop my clogs in the next couple of hours, will someone be able to find my work? Will they understand how to set it up, what it does, how to fix it if errors occur? Would it be simpler for them to rewrite from scratch everything I’ve written to date?’

    Every minute of work that you do is utterly worthless if it isn’t backed up, or if nobody understands it. It is well worth investing at least a few minutes in writing a README.md that explains the purpose of your code and how it can be used.

    Documentation isn’t the be-all and end-all, however, and nobody will be able to read your README if they don’t know where your project lives, or even that it exists at all.

    I’m not saying that you should email your last will and testament at 5pm every day to everyone in your corporation. But do make sure that you make life as simple as possible for whoever lands the unfortunate job of overseeing your project if you’re not around. Do they know where everything is? How to set everything up? What to do, who to contact and where to go if something goes wrong (or right)?

    If you disappear off the face of the earth today, will the organisation be able to cope tomorrow? Answering this may mean going as far as training another member of staff so that at least one other person knows [insert obscure programming language here]. Or making sure another developer knows a password to the system. Make sure you cover every eventuality.

    We all want to be indispensable to our employers. The very best developers achieve this by being as dispensable as possible.