Why Must I Slay This Dragon?
There’s a very straightforward reason why some code needs to be refactored.
It’s too painful to maintain as-is. Nobody knows what the code does. If you attempted to rewrite the code from scratch, you might miss several important (but obscure) edge cases everybody has forgotten about.
Refactoring in a Nutshell
The entire refactoring process looks like this:
- Make one simple change.
- Make sure you haven’t broken anything.
Let’s dive into each of these steps.
1. Make One Simple Change
Because you’re working in a scary codebase, you need to find ways to limit the risks you’re taking.
Making small changes that are easy to understand is relatively safe. Avoid making large, sweeping changes that may have some incorrect assumptions baked into them.
Some examples of simple changes:
- Renaming a variable.
- Moving a function from one class into another class.
- Pulling a couple of lines of code into their own function.
- Removing a dependency (assuming you’ve already removed all calls to that code).
- Creating a new, empty package or binary.
2. Make Sure You Haven’t Broken Anything
Depending on the codebase you’re working on, this can be a non-trivial task.
Add test coverage before you refactor the code. That way, you can make sure that the behavior before and after each refactoring step is the same. Use code coverage tools to make sure your tests exercise all code paths.
If you’re unlucky, you’re working with code that’s either very hard or impossible to test automatically. I’m sorry to say that you’ll have to test your changes manually.
If it’s painful enough, you might want to come up with some creative ways to dig yourself out of this predicament. Are there any workarounds? Unconventional approaches for testing your code?
Ideally, you’ll commit to your local branch after every refactoring step. In practice, you’ll likely bundle several changes together.
If a single refactoring step touches many files, I usually try to get that change checked in to the main branch as soon as possible. The more files you’ve changed, the higher the chances that you’ll run into merge conflicts if you wait before checking in.
Code reviews with 50+ files in them can be annoying for teammates to review. If all you did was to rename a function, and thus all 50+ files were changed in exactly the same way, then the code review becomes very straightforward and quick.
If you’re working in a scary codebase, you want to avoid unnecessary risks. Large, convoluted code changes are a bad idea. Make sure it’s very easy for your teammates to understand all the changes you’ve made.
Beware the next deployment: avoid accumulating a giant mountain of changes between deployments to production. The more changes go into production all at once, the higher the risk.
Why Refactoring Is a Treat, Not a Trick
One of the key reasons I enjoy refactoring so much is the unpredictability of it.
You’ll discover secret relationships and hidden gems. Some of the code will make you laugh. Other code will make you cry inside. You can learn from other people’s mistakes and compile a mental list of “what not to do”.
There are many creative paths you can take on your refactoring journey. As you gain more experience, you develop better hunches as to which path will take you to your goal most quickly.
Getting Stuck in the Maze
I don’t always know where I should start. So, I just pick a place and try some things.
If I feel extra stuck, I start by simply updating the code so it follows my team’s coding standards (the rules that describe what “clean code” looks like in your team). As I make cleanup progress, I become aware of additional things I’d like to change.
Sometimes, I end up realizing that I went in the wrong direction. But even when that happens, I’ll have learned some important things about the codebase I’m working in. It’s not wasted effort.
If anybody tries to tell you that the next step is always obvious, they’re either inexperienced, somehow know stuff that nobody else knows, or they are lying to you.
You’ve Tamed the Monster
Congratulations! Enjoy it while it lasts.