One of the most common refactoring moves is Rename Method.
Automated Refactoring Is Magic
It’s likely that if you use a good editor, there is a built-in refactoring tool that will do a semantic search and replace – even if you have 20 places in code where there are functions with the same name, it will rename just the one you specified and update all of its references precisely.
In most JetBrains editors, you press shift-F6, type the new name, and it’s done. It updates the function in context and also any references to the item within the code base without you having to do any searches. It’s almost magical.
You run the tests, they all pass, and (provided the method wasn’t published as part of some API that clients need to know about) you’re done!
Many people use a text editor that doesn’t have any semantic awareness or refactoring support to speak of. That’s a shame because everything gets more complicated when it’s not just a gesture.
People stuck in a mere text editor might be tempted to “just” do a global search and replace, which leaves the code unrunnable (which means untestable) and broken until the change is completed in every file.
This risks changing some textual occurrences that don’t actually refer to the method being renamed: breakage that has to be repaired before the code will compile and run.
There is a story going around that a (here nameless) book company wanted to change the term “pants” in an American English book to “trousers,” since “pants” is a term used exclusively to describe undergarments outside of the US. In doing so, they introduced the UK readership to words like “particitrousers” and “occutrousers.”
If there is incremental search-and-replace, then the editor will show a programmer each potential replacement, and the programmer must carefully read the code to determine if the replacement is proper. If they make a bad substitution, they’ll have to correct that “occutrousers” error by hand.
That’s not refactoring, as we know it. That’s just changing stuff.
Refactoring is orderly and disciplined, and it never puts the code in a broken state. The tests can run and should pass after each step of the recipe.
The Manual Procedure
The recipe for rename method is presented in the book Refactoring: Improving the Design of Existing Code, by Martin Fowler, Kent Beck, John Brandt, William Opdyke, Don Roberts, and Erich Gamma (all names you may associate with eXtreme Programming, patterns, and software craft).
Here is a summary, with some text adapted from a RefactorPro article.
- See if the method is defined in a superclass or subclass. If so, you must repeat all steps in those classes too. (and run the tests)
- Create a new, empty method with a new name (and run the tests).
- Copy the code of the old method to the new method (and run the tests).
- Delete all the code in the old method replacing it with a call to the new method (and run the tests).
- Find all references to the old method and replace them with references to the new one (and run the tests).
- Delete the old method. If the old method is part of a public interface, don’t perform this step. Instead, mark the old method as deprecated (and run the tests).
You can compile all the code and run all the tests after every baby step, ensuring that the code is “mathematically equivalent” to the degree that the code is tested at least.
This isn’t just “running all over the code base replacing stuff.”
It is not just “writing a replacement that I like better.”
But… all those intermediate states…
The process has some untidy intermediate states and forms:
- You start with a useless empty method that has the new name (dead code)
- Then you have duplication (a smell)
- Then you have a useless ‘forwarding’ method with the old name (lazy code)
- Then you have some code calling the old method and some calling the new (inconsistent, possibly “black sheep”)
- Then you have an unused method with the old name (“dead code”)
- Then you’re done and those smells are history.
It’s like cleaning a closet. You may have to make it worse (pull everything out into the room) before you make it better (put it back in order).
The transitional steps are temporary, and temporarily introduce smells to the code, but they are very short-lived “scaffolding” steps. You achieve the desired result quickly enough.
You won’t be making errors though. Or, if you do, you will spot them right away (running the tests after every baby step makes it easy to know you’re doing the right thing).
Refactoring is disciplined and precise.
Refactoring is not just “rewriting it differently” but rather “transforming code structure via a series of safe and precise transformations.”
Refactoring is Safe
Does it seem like a lot of work when you could have just run around the code base typing stuff over?
It may well seem that way.
Refactoring is safer and more certain, though. You should NEVER have to study the code or pull out a debugger as part of renaming a method.
Safety tends to translate as speed, but it feels like going slow. It feels like extra busy work, but it works every time.
It never causes breakages that you have to spend the afternoon debugging.
The path from the initial state of working code to the desired state of working code no longer passes through the dark valley of “oops, it doesn’t work now and I’m not sure when it will again.”
Isn’t that certainty worth it to preserve your timeline and reputation?
Give proper refactoring a shot next time you’re renaming a method without an intelligent IDE.
To learn more about refactoring, you might consider the Industrial Logic eLearning devoted to the topic, or maybe the boxed set covering Testing and Refactoring. We also offer courses in technical excellence.