Refactoring is a critically important concept in software. Anyone involved in the software industry should understand exactly what refactoring is and why it’s valuable.
Refactoring is not just the redesign of code. The redesign must behave exactly in the same way as it did before. In many situations, this also means bug-for-bug compatibility as well as expected behavior compatibility.
But refactoring is not just changing an implementation without changing its external behavior. Refactoring is also about doing it in small and safe increments. This is extremely important because we never want to be far away from a buildable system. Therefore, we go to great lengths to ensure that were taking baby steps in verifying what we’re doing by running our tests against the system after each baby step. This is important for leveraging feedback in making sure that we’re staying on track.
Whenever I have code that I can’t compile, I feel a bit vulnerable. For this reason, I try to implement my features in the smallest increments I can. There is a lot of value in taking baby steps. Today’s tools and machines are so good that we want to run them all the time and gain the benefit of constant feedback.
Ideally, other developers will have written good extensible code but that’s not always the case. When we encounter poorly written code we have the option of cleaning it up. Many of the automated refactoring options that are available in most IDEs are extremely safe to use, even with legacy code that doesn’t have good test coverage. You might be surprised at how much progress you can make in understanding and clarifying a legacy system with just the automated refactoring operations Rename, Extract Method, and Extract Class.
I know that there is a stigma around legacy code in many organizations because developers have tried to work with it, only to find that they introduced more bugs than they fixed. There is a lot of badly written code out there and there is also a lot of code that was written for one purpose and now it’s trying to be repurposed for something else. Before we add a new feature we often have to clear a space in the existing system for the new features. We do this by refactoring the existing system so that it can accommodate the new feature. This may involve adding abstractions or even introducing polymorphism.
When adding features to an existing system, I always recommend working in this way by first refactoring the code so it can easily accommodate the new feature and then adding the new feature.
If we have a set of good unit tests in place then when we’re refactoring the code we can do it safely because we know our tests have our back. If we write good behavioral tests and we refactor the behavior then our tests shouldn’t break. Instead, our tests are there to support us and catch any mistakes that we make. This is one of the places that the value of unit test for software development comes into play.
But even if we’re working on legacy code without unit tests we can still do many of the safe refactorings, especially if they’re automated in our IDE’s.
I think that refactoring is one of the key skills that developers need and will need in the future. Understanding how to safely go from one design to another design in code is amazingly freeing because it means that we can apply known and safe transformations to making our code more readable and understandable.
I want to help tear down the stigma around working with legacy code because most of the time the fear that we have is unfounded and unproductive. If we learn some simple techniques for refactoring legacy code safely then I think we can all sleep a lot better at night.
{1 Comments }
Previous Post: « Read, Write, and Refactor
Next Post: Pair Programming Pointers »
The main concern for refactoring is the purpose of it. What is the problem we are trying to solve? Is refactoring the best way of solving the problem? Is it going to save development time, memory consumption, responsiveness or will it make developers lives happier? Any reason is good but is the refactoring most efficient way of solving the problem. Quite often developers just want to redo the oldest piece of code just for the sake of “housekeeping”. And then the risks are not worth the effort. But when you have a purpose good agile refactoring practices are crucial. Very good article.