A refactor without tests is a rewrite
Originally published on hayatibis.dev , where the figures move and the toys are interactive. “I’m refactoring it” is one of the most stretched sentences in software. It gets used for renaming a variable, and for a service that is down for a week while someone cleans it up. Martin Fowler, who wrote the book on it, puts it more precisely. A refactoring is “a change made to the internal structure of…
Refactoring, without proper tests, can easily turn into a rewrite. Martin Fowler, a renowned expert in the field, defines refactoring as "a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior." In other words, the code's functionality remains the same even after the internal structure is altered.
Fowler's definition raises an important question: how can you be sure that nothing has changed? Trying a few things in the app or reading the diff may not be enough if the change is significant. That's where tests come in. They capture the current behavior of the code and allow developers to quickly identify any deviations after a change. Without tests, "the behavior didn't change" becomes a mere wish rather than a fact.
Fowler suggests a simple test for refactoring: if someone says a system is broken for a couple of days while someone is refactoring, it's likely that the refactoring is not being done correctly. He refers to this untested kind of change as "restructuring" rather than refactoring.
To mitigate this risk, Fowler recommends planning the change with the same care one would give to a rewrite. This involves taking small steps, each one checked with tests. The process should be a chain of small moves, such as "Rename Variable," "Extract Function," or "Move Function." After each step, the tests should be run to ensure the behavior stays the same.
With the advent of AI-assisted coding tools like Claude and Codex, refactoring processes have become more straightforward. These models are already familiar with the concept of writing tests and iterating until the code's behavior is correct. However, developers must still guide these tools to adhere to their own best practices for refactoring.
Naming a rewrite can help clarify the scope of the change and plan it more effectively. One approach is to run the old and new code side by side and compare their outputs, gradually transitioning traffic behind a flag to the new code while keeping the old code accessible for a fallback. Martin Fowler's "Strangler Fig" pattern is a gradual method of implementing this approach, allowing new code to be grown around the old code incrementally until the old code can be eliminated.
In summary, refactoring without tests is essentially a rewrite, carrying the risk of introducing bugs that may go unnoticed for weeks. By writing tests, developers can ensure that the code's behavior remains consistent throughout the refactoring process, minimizing the likelihood of a full-scale rewrite.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.