This post is from the CollabNet VersionOne blog and has not been updated since the original publish date.
Refactoring with Fire
You are a responsible agile developer, practicing TDD and keeping your code clean through refactoring. Your “bar” is always green, but are you living and developing in a world of “false green”? Is your green bar continuous or is it only measured at points in time – when you run your tests, for example? At the Lean Conference in April, Joshua Kerievsky gave an excellent presentation titled “The Limited Red Society.” A summary of this presentation can be found on Joshua’s site and the video can be found here. Synopsis: In application development, we need to be aware of the state of our code at all times, not just when we run our tests. In the real world, things may break at non-ideal times, like during the middle of a refactoring, and we must fix them. Joshua’s session really hit home about a month ago when I felt like I was refactoring with fire.
To set the stage, I was working solo on some custom code for a customer. I developed it test-driven and had solid tests. After I delivered the code to the customer, I saw a great spot for refactoring and simplifying the code, so I dove in. Then the customer came back with a change…but I was in the middle of refactoring! The change would only take me about 10 minutes but the refactoring session would last another hour or two to get to the point where I wanted it. Right then I felt like I was refactoring with fire. This was only a 10 minute change, but a production emergency could go on quite a bit longer and I would be leaving my codebase unstable. I vowed to not let that happen again.
Fast forward to last week. I am working on some more custom code for a customer. Without getting into too much detail, the application was building relationships of work from VersionOne. The application was originally implemented with one level of relation – parent to child. After some discussion, we came up with a story where the relationship would need to be up to eight levels deep (parent to multiple children to multiple children and so on). My tests are solid so I was comfortable making the change, but I knew it would impact quite a bit of the system. If I was to make the relationship-building recursive, it would also impact how I query and report out data. What to do…I said “Don’t play with fire.”
I used Parallel Change in this approach to make sure the codebase was available in the event of an emergency. In the software development process, Parallel change is creating and working on new functionality without connecting into the main process flow. We have all used parallel change before. I wasn’t using it often enough.
Here is what I did … nothing groundbreaking mind you, but by employing this approach, I was able to deliver my code continually while the large change was worked out.
- Create new tests for my new recursive relationship builder (using mocks to simulate the result from the query)
- Implement enough code to make my test for the relationship builder to test (all older tests will be passing and remain passing as they haven’t been modified)
- Add more new test cases around the relationship builder
- Add code to make these tests pass
- Rinse and repeat for the other new areas of functionality needed – the query for the data and the structuring of the data for reporting in this case
- Once I have completed the tests for the independent levels of functionality, write the integration tests around the cumulative effect – query, build relationship, structure data for reporting
Only after my tests pass in that last step do I cut over my app to use this new flow. With this approach, my code could have been put on hold at any point and been stable for a production release.
Challenges with this approach:
As with any code, higher coupling and multiple responsibilities in objects and methods will make Parallel Change more difficult. Think of a highway. If it is a simple highway (2-way traffic, not multiple on and off ramps) then building a parallel highway is simple – you run a new straight stretch of road, and once it is done you cut over the entry and exit points for the new road. Just like your code – if it is a straight run, Parallel Change will be easy.
If the highway has multiple on-ramps, off-ramps, a railroad crossing, and a wandering herd of yaks in the middle – then building your parallel road to that is going to be very difficult. With that scenario, you’re obviously better off simplifying the highway before you try and create the new stretch of road.