My colleague, James Hulgan, recently posted his feelings on refactoring which drew some interesting comments from the blog-o-sphere. Having been exposed to many refactoring arguments throughout my career, I’ve decided to add my thoughts on refactoring and some examples of where I have seen it work and where it hasn’t.
In his post, James writes that “refactoring – standing on its own – adds no value, so it is not a feature, and shouldn’t be added to the backlog.” I could not agree more. Refactoring for the sake of refactoring is a grossly insane practice that results in zero value being delivered to the product. However, refactoring by definition, is inherently supposed to deliver value. If by investing X amount of time now in refactoring we see a 2X reduction in development effort down the road, everyone wins!
In theory, refactoring is a great thing. But in practice, you must be extremely careful to ensure that it is delivering value. Otherwise, you can find yourself with large refactoring projects that, in the end, have delivered no value.
So here are some tips from my experience that have helped Product Management and Engineering live together in harmony when it comes to refactoring.
Quantify the Value of Refactoring
Refactoring is typically justified by delivering value elsewhere; whether through making new features and functionality easier/faster to build or delivering some other benefit, such as easing the upgrade process or minimizing support burden. All of these are very valid reasons to take on a refactoring project. However, without quantifying the value in some way, you have no concrete evidence to prioritize the refactoring project high on your backlog.
I have found success in ensuring that all refactoring projects have their return on investment quantified. If concrete numbers are not available, estimates are better than nothing. For example, a refactoring project estimated at 6 man-weeks that results in decreasing your company’s support burden by $450,000 per annum is an obvious winner and that return can be compared to any other project promising a revenue increase or cost decrease. However, many refactoring projects promise the ability to make future development more rapid. These are typically harder to quantify.
An approach that I have taken is to have the development team review the product backlog that has already been estimated or sized and provide new estimates as if the refactoring project had been completed. You will be amazed what comes out of this process! On one occasion, after a development team insisted for over four sprints that a refactoring project was needed, they came back and told me that it wasn’t worth it after looking at the refactoring project’s minimal effect on future development efforts. In another situation, a team accelerated the timeline on a refactoring project after we assessed that for a total of 12 story points of refactoring effort, we would reduce the effort of the near-term product backlog by over 60 story points!
Create a Closed Loop Process
Quantifying the benefit of refactoring projects typically involves some degree of estimation. Therefore, it is of utmost importance to ensure that the process is closed loop. If the engineering team estimates that a refactoring project will take 12 story points to complete and will reduce the backlog effort by 60 story points, you absolutely need to measure what the actual benefit was on the back end. In my previous example, we found that the 12 story points of refactoring actually reduced the near term backlog by only 53 story points.
Obviously we were still happy with the results but the point here is that our constant measurement resulted in the expectations around any refactoring project’s return to be realistically estimated. If you don’t compare the estimates to the actuals, promises of large returns can be made but never delivered simply because the development team really, really wanted to do some refactoring of their code.
Earmark Development Capacity for Refactoring
My greatest success when it comes to refactoring was dedicating some development capacity, each and every sprint, toward refactoring. I tried to target up to 20% of capacity (yes…you read that correctly…20%!) So if a sprint team had a velocity of 30 story points, we would allocate 6 story points to refactoring/technical debt each sprint. Suffice it to say, this was a very hard sell to the rest of the executive team. Typically refactoring projects, if justified, were taken on as they came along. At that company, we spent only 8% of development capacity in the trailing 12 months on refactoring/technical debt and here I was asking for 20% each and every sprint.
So how did we sell it? We did our homework. My Chief Architect and I went through the trailing 12 months of actual development effort across the entire development team and modeled a number of scenarios. We wanted to see what the past year would have looked like if we kept our refactoring steady at 5%, 10%, 15%, 20%, and 25% of development capacity for each sprint. What we found was amazing! Our models showed that when allocating refactoring efforts across every sprint, regardless of the percentage of overall development capacity, the overall refactoring efforts were reduced.
The logic around this is pretty straightforward and can be illustrated with an analogy that we used for our business case at the time. Assume you have a car and change the oil every 3,000 miles. All else being equal, you won’t have any major repairs. However, if you don’t change your oil, you will save quite a bit of money and time initially but are much more likely to have a very large repair bill down the road for neglecting your car. The oil changes, although they seem to provide no immediate benefit, are actually ensuring that in the long term we are saving money.
In our analysis, spreading refactoring work out meant that we were able to reduce development effort required on the majority of feature-driven projects. In aggregate, we would have shaved off 32% of the time spent on features in the previous 12 months which would have enabled us to deliver many more features in the same amount of time. Plus, we would have avoided dedicating entire sprints to refactoring efforts to ensure that sellable feature delivery was consistent.
Please note that this approach is not a magic bullet. Each organization is different, and I highly recommend that you perform your own assessment. When I initially launched this years ago, we found that 20% provided the greatest return for us. Over time, we reduced it to an average of 14%. Additionally, just because you are allocating capacity to refactoring every sprint doesn’t mean that you are giving your development team Carte Blanche to do what they want.
We managed 2 separate product backlogs when we implemented this program: a standard product backlog and a refactoring backlog. User stories only made it into the refactoring backlog if they passed our return on investment justification outlined previously. We allocated a maximum of 20% to refactoring. If there were not enough approved user stories in the refactoring backlog to total 20% of capacity, we took on additional user stories from our standard product backlog for that sprint.
I hope that you find my experiences with justifying refactoring helpful. They proved to be a great compromise between product management and development while delivering real results for the business.