I’m working on a fairly large codebase right now. Unfortunately it has significant problems. Right now we have the time to work through some of these problems, so we are. What does that mean? Refactor, refactor, refactor.
An unfortunate addition to our poor codebase is the lack of unit tests for huge portions of it. Not the perfect situation when you’re starting a major refactoring exercise. So what is the best way to handle major refactorings which lack suitable test coverage?
After a couple of weeks working on refactoring I think I’ve managed to screw up a couple of things well enough to have some lessons learned. The first attempt we made at a major refactoring we started working on a method that was a major culprit for fragility and complexity. We refactored and refactored in that method. By the time we were done, the guilty method had been reworked, but we’d failed to see the logic in delegated calls. Not only did we miss that code, but we didn’t see the patterns that were embedded in the existing code.
In the end we managed to work our way out of our pickle and get a nicely formed code base in that area. The next set of code that we refactored was approached in a different way though. Instead of diving into the problem area and ripping and tearing, we opened up the code and mapped the high level functionality first. We traced the majority of the delegated code so that we understood more than the skin of the problem. The last step was coming up with a game plan. The game plan wasn’t anything formal. Instead it was a rough idea of how we wanted to interface with the problem code (single line call or calls to multiple discreet public methods or other means). We combined that with an overall riding guidance to testability and dependency injection we ended up with the basis for our approach.
One of the challenging things that we’re running into is refactoring that removes an excessive use of Singletons and static classes/methods. Refactoring isn’t as straight forward as changing a class/method name and then working on the logic that it is abstracting away. Instead we’re spending a lot of time finding the usages and figuring out how we’re going to remove the single line usages that were originally coded. It’s not a huge problem, but it adds to the workload. Thank goodness for ReSharper and Alt-F7 (Find Usages).
Using this approach we managed to get the code into a more respectable state on the first pass through. I don’t know if it’s the best result, but so far it’s proving the most effective for us.