Rehearsal is the key to a good performance.
Revision is the key to good writing.
Refactoring is the key to good software.
You get to Carnegie Hall with practice. Practice is rehearsal. The direction of rehearsal is towards constant refinement. You are always making the performance clearer, more specific, effortless to understand and enjoy.
The profession of writing is so thoroughly about revision that it’s regarded as the sum total of the art. Fantasy author Patrick Rothfuss will revise his novels more than 8oo times before he’s done. Check out his revision of a fan letter asking him how he revises and why.
How Rothfuss revises a fan letter is how a crafter refactors software. She will make the functions more cleanly abstracted, better named, more straight-forward. Shorter. Shorter. Even shorter still. Bugs hide in lengthy code blocks. Confusion hides in wordy prose.
Why refactor? That might be where the art for novel writing is, but for software development, the end user never sees your effort. Is it for other developers? Your future self coming back to the code? Partly. But it’s not just for aesthetic enjoyment. It’s because software needs to change.
Patrick Rothfuss will revise and revise and revise. But at some point, a point only he knows, he sends his words off to the printer. Once his words are in the book stores, they might as well be written in stone. To change them, even if Rothfuss himself does it, would be like touching up the Mona Lisa. George Lucas learned that lesson the hard way when he went back to revise Star Wars. The Star Wars Wars rage to this day among the fandom.
Software is different from novels or movies. It is expected to change and adapt itself to the needs of the moment. It is never done. Revision in software, refactoring, is the art of making the code ready to accept changes in the future without needing a complete rewrite. Refactoring must be done constantly, and in small steps as new features are added.
At 8th Light, I’ve been reading Clean Code by Robert C. Martin. The chapter on Successive Refinement walks through a very similar process to Rothfuss’. It offers invaluable practical advice that takes advantage of software’s capabilities versus prose.
For one thing, Test-Driven-Development (TDD) should be used throughout. In prose, how you feel rereading the words is the test. But in software, tests can objectively validate your work. And writing the tests first puts you in the right mindset for avoiding the software equivalent of murky paragraphs and run-on sentences. This allows you the freedom to reorder methods and change names without fear of irreversibly breaking things.
You might have noticed that Patrick Rothfuss’ revision of the fan letter conveyed exactly the same information as the original one, albeit in a clearer way. That’s the purpose of refactoring software. The code should always be in a state of running order. Even if it’s messy, if your boss wants to see a demo at any arbitrary moment, you should be able to oblige her.
Another key is taking small steps. The greatest human alive has achieved a standing jump of 12 feet, 2 and 3/4 inches. I can jump 5 feet, 6 inches. But the average human walks about 110,000 miles in her lifetime. That would circle the planet more than four times. Small steps have allowed humanity’s influence to envelop the Earth and reach beyond the moon. A giant leap of a little more than 12 feet pales in comparison to that power.
An ambitious but impatient programmer might try to add a major feature all at once, ripping out working parts of the code and then struggling to put it all back together with the new feature included. This mutilates the code at best. At worst, the app breaks and the mess makes it nigh impossible to be fixed.
Take little steps instead. Run the tests after every tweak. Tests need to be fast enough to return results without interrupting your train of thought. If a test fails, make it pass before proceeding to the next step. Writing software well is very similar to Patrick Rothfuss’ revision process. It is a game of many small tweaks that add up to much more than a giant leap.
Adding features to clean code makes a mess. Because the code is clean, the addition is possible, but adding a new feature is always an act of violence. Keeping the code abstracted, modular and isolated can minimize that violence, but extending the code’s functionality will always mess something up. For this reason refactoring must be a constant effort. If you extend the code without refactoring, it will become too brittle and convoluted to make changes. Progress will grind to a halt.
To the impatient, it might seem that time spent testing and refactoring is wasted. It’s only wasted in the sense that time spent tying your shoes is wasted. You’ll trip. Take the time to tie your shoes. You’ll waste a lot more time if you break an ankle. Likewise, after every change to the code, take the time to refactor.
Sometimes you’ll put something in only so that you can take it out again. Rothfuss does this too. It’s not a waste. It’s an important part of revision. What matters in a novel is the story. The words written are only a means to that end. What matters in software is its usefulness. The code written is only a means to that end. Refactoring might mean deleting a freshly-written function, class, or module, but the solution progresses indefinitely.
To refactor well, you need a direction. In theory, you could refactor to make the code messier. The goal is to make changes that, though the compiler doesn’t care, future you, your team mates and your product owner will. Clean Code’s chapter on Emergent Design gives three stars to navigate by:
- Don’t Repeat Yourself. Be on the lookout for repeated phrases. Repetition is a major hideout for bugs. You should only need to edit one piece to make a complete change in the code. Having to edit multiple areas will cause confusion. Something will be overlooked. If you have to make multiple edits for a single change, it’s a good sign you need a new abstraction. This can be tricky. There are myriad ways in which code can be unintentionally repeated.
- Make the code expressive. Name everything well. Make it very clear what your methods do. Define methods just below their first use so that others don’t have to hunt through the code. Make it so that there are no surprises or confusion about what your code does. Expressive code is easy to understand. Good names will tell you how to improve their code’s quality.
- Make the code simple. Without repeating yourself, without glomming your code into an impenetrable sphere of mystery, and without losing the expressiveness of your names, make the code as simple as possible. The less your code resembles a Rube Goldberg machine, the better. Write less. Every bug ever written was written.
Refactoring is never done. To write code is to write bad code. That’s natural. It’s in the cleaning that craftsmanship emerges and code becomes easy to extend. Code extensions are also bad code. So refactoring must be a continuous practice if you want to maintain software that is responsive to change.
The art of prose is in the revision. The art of performance is in the rehearsal. And the art of coding is in the refactoring.