Refactoring: where to draw the line?
Refactoring is a something we spend a bit of time on at Highgroove, so much that David recently wrote about refactoring and related tools in Red, Green, Refactor and I’m writing with even more on refactoring and the ‘Refactoring Rabbithole’.
“When refactoring, it’s easy to see 10 other things that probably should/could be refactored”
Spending time on refactoring is a tough sell to clients: by definition, the ‘backend’ can be completely re-done without any visible changes in an application. As the complexity and size of a project grows, refactoring can be necessary to keep things as simple as possible, keep the code quality high, and keep the number of bugs low.
Refactoring should be done to address needs, but due to the huge number of patterns, developer habits, and differing intentions, it’s easy for developers to get carried away. In the worst possible case, you end up with ‘the rewrite’ where part or all of the backend of an application is rewritten from scratch. This is typically not cost-effective, but is actually needed occasionally.
More frequently, a developer will be implementing a small feature and see ‘one small thing’ to fix. Then they see a related method that could be more readable, some inconsistent naming, and before you know it they’ve spent the entire afternoon going down the rabbit hole and touching every file in the project.
A recent project that Highgroove took on to get in ‘launchable’ shape looked like it was going to just require a little bit of polishing and a ‘new sign-up flow’, but ended up not having any test coverage and had a lot of code from it’s previous development team that was overly complex (some of which was intentionally obfuscated to make it harder for ‘hackers’). Our customer wanted to launch their product ASAP, but there were some features they needed that weren’t yet built.
We explained their current situation, testing, and refactoring, to the customer and outlined several options:
The “Nuclear” option – Redoing the application from scratch, and doing it in the Right Way following industry standard best practices.
The “Cross Our Fingers” option – Building the new sign-up flow, but sticking with the pattern of the rest of the system and not writing tests because we couldn’t adequately test new features independently without touching the rest of the system.
The “Refactoring” option – Identifying parts of the system that we would need to interact with, writing tests for them, and refactoring them as needed to enable clean integration with the new features we’d be developing.
Hopefully you can guess: they chose the last option. We spent several iterations adding tests, improving code quality, and once we got the point where we could start adding the new functionality, we stopped. There were still random abbreviations, huge swaths of untested code, and things that we didn’t understand, but our client had a time budget that we needed to stick to. This was hard! As developers, we like doing things the Right Way which means shipping high-quality, well-tested applications. In this case, we had to settle for something much less than perfect.
For every major issue we identified, we updated the README with suggestions for future development, and for every issue regardless of size we created stories in pivotal tracker, anywhere from “Refactor the Foo model to replace all occurrences of SOS with SaveOurShipMessage or save_our_ship_message” to “Figure out what the baz method does, give it an appropriate name, write tests, and refactor for readability.” Some of these things got done as we wrote code that interacted with them, and many more are still in their backlog, hoping to be eventually done.
Just like any new functionality, for any refactoring ideas we make stories for each one. We keep these stories as descriptive and simple as possible, and they get prioritized by the client like any other stories. Seeing these stories pile up in the backlog and knowing that the issue won’t be forgotten is usually enough to keep me from taking refactoring further than I need to.
How do you control your insatiable urge to rewrite everything? How do you sell refactoring to customers?