When you think of refactoring, you imagine it in the context of TDD. Before working on a feature, you first write a failing test. Then you make it pass by writing code that implements that feature. Finally, you refactor the working code to make it more readable, clean, and beautiful.
In our industry, it’s known as red-green-refactor. You start with red (failing test), and move to green (working code) code, ending with refactored code with a better design. However, there are some other times when refactoring code can be very useful. This post explores a few.
Most of the things in life follow Sturgeon’s revelation.
90% of everything is crap.
As software developers, we read a lot more code than we write code. If you take the above law at its face value, that means the code you will read will often be messy, difficult to understand, and hard to read and maintain. The developers who wrote it are long gone, and when they wrote the code, they didn’t pay attention to writing it well.
It doesn’t even have to be others’ code. Maybe you wrote that 100-line method during the last deadline and never got around to rewriting it to make it better. Now you are trying to read that mess, making sense, thinking, “what was I thinking?”
This is a great time to refactor. When you see messy code, you should go ahead and fix it. You do something about it. This is how you can fight the software entropy, the idea that software quality usually degrades and rots as time goes by and new features are added to it. The pragmatic programmers called it the boy scout rule:
always leave the place cleaner than it was before.
The changes you are making needn’t be massive. Maybe you are renaming a method by giving it an intention-revealing name, adding comments to explain why something was done that way, or extracting a method to the right level of abstraction, I could go on.
Ultimately, you are trying to make the code better.
Now let’s come to the remaining 10% of the code, the good code. But that doesn’t mean you will read it and understand it immediately. In fact, you are reading the code to understand it. Maybe this code is part of a significant feature that your ticket is based on or the code that you will write needs to use some component from that code, and you need to figure out how it works. My point is,
You have to read and understand the existing code to write new code.
So there you are, reading blobs of code. You are trying to follow the control flow, analyze the loops, hold the state in your head, figure out which object has which responsibility, and delegates to which object; I could go on. But after a while, you understand the code. All the pieces start to fall in place. The code makes sense. You know what’s going on. I love these aha moments once I understand how a complicated piece of code works.
This is a great time to refactor that complicated piece of code. Don’t just move on to the next thing on your list. Because you will forget it in a few days, and most likely, you will be re-reading that code again sometime in the future.
If you don’t move that understanding from your head to the code itself, you will have to go through this analysis again the next time.
Once you understand some obscure code, you have to refactor it, add comments, and make it more readable so you don’t have to understand it the next time you read it.
But AK, what about “if it ain’t broken, don’t fix it?” To answer that, I highly recommend you read Martin Fowler’s (who is one of my programming heroes, right next to DHH) essay “Is High Quality Software Worth the Cost?”. This essay tackles the age-old problem in our industry,
should you spend time improving software quality or focus on releasing more valuable features?
Here’s the 2-minute summary:
- Neglecting internal quality leads to a rapid build-up of cruft.
- This cruft slows down feature development.
- By keeping internal quality high, a team can keep the cruft under control.
- High internal quality allows a team to add features with less effort, time, and cost.
In other words,
High quality software is cheaper to produce.
However, does that mean you should refactor whatever code you encounter that doesn’t please you? Leaving aside whatever stuff that you were working on in the first place? What if you see some other piece of code that needs attention? You’d be just running from one method to another, one class to another, not getting anything done.
Here’s one way to go about it. When you see some code that needs attention, you make a note of it and continue working on your stuff. It’s like meditation. When you are meditating, thoughts will come into your mind, but you focus on your breathing, and they will pass as leaves on a stream of water.
However, in this case, you have to remember (or better, write down) the pieces of code you want to improve. When you finish your original task, create a new branch, ensure there are tests to catch you if you make a mistake in refactoring, and then refactor the code. Refactor the code in isolation, in a separate branch, with tests to prevent unintended side-effects.
Any fool can write code that computer can understand. Good programmers write code that humans can understand. - Martin Fowler