Another strategy to help you justify refactoring is to do it in order to make small improvements in an existing system. Don’t try to bite off the whole Apple at one time. Really bad legacy code got that way over long periods of time and in my experience, a similar, gradual approach can be good for making it better.
Rather than trying to fix an existing system all at once, consider just making it a little bit better each time you have to go in and work with it. You don’t have to do much but just change the direction of decay. Instead of letting a design get a little bit worse each time you have to go in and add new features, find ways of improving the design to accommodate the new features. Improve the design in the area that you’re working and then also improve other parts of the design while you’re there if you can do it easily.
As a high ranking tenderfoot in the Boy Scouts of America, I was a devoted camper but not into merit badges. My scoutmaster told me words that I will never forget. He said, “Always leave a campsite cleaner than when you arrived.”
We didn’t just clean up after ourselves when we left a campsite, we also did a little bit more. We looked for other trash or garbage that other people had left and we just took a few minutes to clean that up too. If every time we go into code we make it just a little bit better then our software would no longer decay but rather it will start to take an upward spiral and improve as we continue to work with it. Wouldn’t that be great?
People think that refactoring an existing system should be a gigantic process but I find it’s best to work on it a little bit at a time and as a background activity. This is often a better approach than shutting down the entire organization to clean up an existing system but sometimes that’s unavoidable.
My preference is to make small improvements on existing systems as needed. If an existing system is poorly written but still serves its function then I’m apt to leave it alone. It’s really only when I have to extend an existing system by adding features that I want to first clean up the code so adding the new feature is more straightforward.
Sometimes, the most direct route to a solution in code is to first clean up a mess before adding new features. Sometimes it makes sense to add features by first refactoring an existing design so that it’s straightforward to accommodate the feature when we add it later. This can make adding the feature much simpler and safer.
This is actually a technique that we call “refactoring to the open-closed.” The open-closed is a reference to the Open-Closed Principle that states that “software entities (such as classes, methods, modules, etc.) should be open for extension and close for modification.”
What this means in practice is that we should write software in such a way so that when we add new features we’re minimally changing existing code, maximally writing new code. When we have existing code that doesn’t allow us to easily add a feature then it can be far more cost-effective and efficient to first refactor the existing code so that you can easily add the new feature later.
There’s a reason that we refactor in tiny steps. When you take a tiny step and something goes wrong then the chances are that you can figure out what happened quickly. There’s only a limited number of things that can go wrong in a tiny step.
Small improvements add up. I’m always surprised at the end of a day when I spend it refactoring existing code using the safe refactorings, like Rename or Move Method, to learn an existing system. I always walk away knowing a great deal more about the system but also I can see real improvements in the clarity of the code and in the design, making it a win for both me and the code.
Note: This blog post is based on one of the “Seven Strategies…” sections in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Previous Post: « Refactor to Learn an Existing System
Next Post: Refactor to Retrofit Tests in Legacy Code »