In a previous post we discussed Propagating Best Practices In Code. To quickly review: if you want to make across-the-board changes to the way a certain thing is done in code, what is the best approach? There is a balance to be had between incremental updates and migrating all at once.
I recently had the opportunity to make a large-scale set of changes in code, and wanted to share the experience about how to strike that balance.
Here’s the background: my project had recently updated a library, and I wanted to replace all the instances of a certain outdated (and buggy) component with a new component supported by the new version of the library. There were 50 or so places where the old component or its subclasses were in use, some of them highly visible to users, so this would be a big change. There was potentially a lot that could go wrong, but correspondingly potentially a lot to gain.
As a rough estimate with stories and story points, I spent about 8 points to design and develop a replacement component based on the updated library, and 2 points each for the first three replacements, unit testing, refactoring, and generalizing as I went. The rule of three applied here, and turned out to be surprisingly accurate. I thought the first component was reasonably generic and ready for use elsewhere, but once I converted two more I discovered use cases and scenarios that required more refactoring and coding.
Having three replacements under my belt, I created an epic with a story for each replacement, assigning story points of one or two points each. Towards the end I even created some stories with 1 point each for several replacements at once. The assumption was that the replacements would get easier and go more quickly at the end as the code stabilized and as I got more familiar with the use cases of the component.
The plan is that the epic now appears on the backlog and the component replacement work has visibility along with everything else – so nobody forgets about it! We should be able to do a complete replacement over the course of several months, with work done and work in progress clearly defined.
As for prioritizing, here is how I decided which components to replace first:
Some of the components to be replaced were both high-visibility and had known bugs, and we knew that replacing them wholesale would fix those bugs. In this case, this was part of the point of doing all this (with the new library) in the first place. Obviously, buggy code is in a prime position to be replaced.
Next, places that had high visibility (but without known bugs) were targed for replacement. Besides being an improvement that is visible, these will be the places people look to for example code, which was a problem we addressed previously. The places that developers can see first are the places they’ll look to for sample code first.
Code that in less-frequently-used and less-visible places went next to last on the list. These are low-priority targets, and will probably only be done if they are visited for other reasons anyway. Overall they may or may not ever get done depending on developer bandwidth.
Code that is in a feature likely to be deprecated or removed goes last. With any luck, that code will go away before you get a chance to get to it!
So there you have it: one developer’s experience of propagating a best practice in code that spans many sprints. Hopefully this will inspire you to make sweeping changes of your own.