Good Software Design - Part 3: Refactoring
Remember this code from Part 2?
There are at least three ways this code could be made better. The choice depends on any known history for this code, the goal of the refactoring efforts, as well as the confidence to try a refactoring or two and get a sense for which will get you most quickly to your goals.
Let’s listen in as Bob and Alice refactor the snot out of the method above.
Alice: “Phew! Long Method!”
Bob: “Yeah. And the loop is doing three separate things…”
Alice: “Okay, let’s try Split Loop.”
Alice opens a browser tab to https://www.refactoring.com/catalog/splitLoop.html just to be sure they don’t miss a tiny but critical step.
Bob: “Hey, cool: We made it worse.”
Bob and Alice now run all the tests to make sure they pass. Refactorings aren’t supposed to change the behaviors, so if a test fails, it wasn’t really a refactoring. In that case, they would undo all changes (e.g., revert from their git repo).
Did they really make it worse, or did they just make some of the duplication even more obvious?
Alice: “This code is going to give me a headache. Let’s do some general housekeeping on all that duplicated code and all the abbreviated variable names.”
Code smells often hide other code smells, so it’s a good idea to trust your own judgment (or that of your Pair-Programming partner) regarding the most obvious or most potent stink; clean that up, then take another sniff.
Also check in frequently with your intended goal. Are you refactoring to better understand the code, to find and fix a defect, or to add new functionality? It’d be great if we could spend months rewriting all our old code so it was “perfect,” but that alone won’t provide demonstrable business value. Without a purposeful destination, even refactoring can become a waste of time.
Bob proceeds to use his knowledge of the IDE’s refactoring hotkeys (mostly Rename Variable and Extract Local Variable), and runs the tests after each refactoring. (Alternatively, there are awesome IDE plug-ins, like NCrunch for VisualStudio.Net, that will run tests in parallel with your editing activities.)
Alice: “Wow. Headache cured. Now let’s clean up that Long Method by doing an Extract Method on each loop.”
Bob: “Okay…Validate Serial Numbers, Install Licenses, Install Drivers… ”
Alice: “And let’s delete those comments now that they’re extraneous.”
Alice: “Much better, yes?”
Bob: “Yeah, but we’ve increased the amount of structural duplication with that Split Loop. I’m tempted to create some lambdas to remove the loops.”
Alice: “I see what you mean. The trouble is, we have teams who use this code but aren’t yet lambda-savvy. It’s a lot more readable than it was, and we’ve made our next enhancements easier to achieve.”
Bob: “We’ve also made it slower by tripling the times we loop…oh, pretend I didn’t say that!”
Alice: “Right. Very few records for each product, and the most time-consuming part of this is the driver installation. The code now has less cognitive load—easier to maintain!”
Bob: “Okay! I’m pushing our commits to the trunk!”
Refactoring Is Design
Refactoring is software design. It’s reshaping the design of the code, not rewriting the code. Typically, refactoring reduces duplication, resulting in less code.
Software design, in turn, is often a mystery to those outside the developer community. It has something to do with how the code accomplishes its functionality, but the tests or specifications do an even better job of that, so what exactly is software design?
A good software design is one that helps the team understand what the code is doing, and allows the team to introduce new functionality without disturbing old. So good design includes robustness (through tests, and by isolating changes), clarity (ubiquitous naming, simple structures, modularity), and maintainability. Less code and less duplication make the system much easier to maintain and extend.
A good design is, therefore, code that passes all the tests, and that the team agrees facilitates change.
In the finale of this mini-series about Good Design, we’ll take a closer look at the role of tests in design, and we’ll also explore how some Agile team practices have a direct impact on the quality of your software design.