Refactoring is quite well accepted in the software development circles. However, some things that are not acknowledged are the challenges with it. No, the refactoring v/s rewriting has been dealt with quite in detail. I am talking of challenges in testing, and committing the code changes to the VCS. Their severity increases especially when the refactoring happens parallely with rest of the feature development.
The root cause lies in the basic nature of refactoring. Refactoring is making changes to a body of code in order to improve its internal structure, without changing its external behavior. Which means that refactoring advantages are for the programmers more than the users. Note that, modifying the code to solve a performance problem should be considered different than refactoring. Refactoring does not want changes in external behaviour, if there are any they are side-effects.
This can be a problem with the traditional testing practices. Most of the testing that we carry out is about the external behaviour, so they become pointless to test refactoring. Testing for refactoring has to be carried out in two steps:
- Ensure that the refactoring changes have happened. There is nothing better than unit tests to do this.
- Ensure that the refactoring changes DO NOT affect the external behaviour. This usually means testing the output against previous versions, using unit testing and likes of regression testing.
For these tests to be effective, it is important that they are carried out independent of the tests for rest of the code changes. Refactoring might have its own bugs, the new development might have their own, and together they can create other pseudo-bugs. It is necessary to separate these two kinds of code changes to avoid such nightmares.
And often this percolates down to the granularity we want in the VCS revisions. It is better if refactoring changes are committed separately from rest of the development. So that rollbacks and merges are cleaner. Not only does this help in testing, but it helps a lot in future if you want to determine the timeline of code changes.
I have put down my own list of steps before I start looking into refactoring:
- Create a branch in the VCS to hold the refactoring changes. This enables us to have a clean separation during development and committing the code changes as well.
- Ensure unit tests exist for the blocks of code you want to refactor.
- Refactor the code.
- Perform unit testing for refactoring changes.
- Carry regression testing for the refactoring changes.
- Merge refactoring code changes with the rest.
- Carry out the traditional testing.
I had never thought that refactoring can be so much about testing and VCS. And I think this is the basic reason behind unpleasant experiences with refactoring. Refactoring is not only about code changes, it has to be carried through the entire process to realise its benefits. If you are planning to carry out refactoring, take some time to plan it out and build your own steps, or better, a process that incorporates refactoring.