The economics behind refactoring

November 26, 2020

Passionate software engineers will fight for code quality and propose refactors from time to time in order to improve the overall codebase and reduce technical debt. The purpose of this article is to challenge that idea by introducing a new variable: the economics behind these actions. Are they needed? To what extent? Are they really worth it?

Definition of a refactor

From Wikipedia:

In computer programming and software design, code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behaviour. 

Refactoring is intended to improve the design, structure, and/or implementation of the software while preserving its functionality. 

Potential advantages of refactoring may include improved code readability and reduced complexity; these can improve extensibility. Another potential goal for refactoring is improved performance.

A bit of software economics

The way risk is managed by the team, the way investments are planned and ROI justified, and the way options are kept open whilst trying to manage uncertainty are some of the economic aspects behind software decisions. And they make all the difference in the long run.

Everything in software has a cost attached to it - including software refactors - so every decision should be framed by taking into account what is the expected value attached to the incurred cost that is being considered. The cost of software refactors need to be justified, it should be economically viable.

Refactors as technical debt payment

Technical debt is a decision that solves a problem in the short term but implies that there will be a cost postponed for the future, with the caveat that interest will also have to be paid. In the context of software you can really notice the effect of technical debt when a simple change takes complex and multiple amendments, you need to edit various files, it breaks other parts of the code or it forces you to run convoluted deployments from various moving parts.

Some past decisions related to the design or the architecture of our system may cause the team to be constrained and slowed down today. Frustration, complexity, excessive time to make simple changes or fragility are some of the ways technical teams have to pay off that debt and its associated interests in the future.

A refactor may be able to pay some of that debt off. However, economically speaking you should keep paying interest if that was cheaper than the value that you could generate with your assets (time, knowledge, energy) otherwise (this is called opportunity cost). That's why tackling tech debt is generally a good practice when there are no peaks in your project, or when there is a low amount of requirements and it's a quiet season for users. If you can’t generate a lot of value, maybe you could try and reduce your debt. Telling this apart is hard sometimes.

If I estimate that a refactor is going to take me 2 hours and it will save 5 minutes for each member of the team on changes that are performed daily, I could easily quantify how profitable this will be and by when that effort will be amortised. However, it is important to try and do this exercise - sometimes developers suggest refactors purely for "quality" or "perfectionism" reasons. We will discuss below, but "quality" needs to be understood, and it also has to be economically viable.

Refactors as investments

A refactor can be seen as an investment when we change our software design, structure or architecture to generate options in the future. For example, we may decide to implement some configuration for different types of payments to make our software more extensible and faster to change in the future, just in case the business decides to support more of those types.

This sounds like a good idea in principle. However, the ROI (return on investment) should compensate for the cost. What would happen in the case above if the business never supported additional payment types? Our refactor would have been a waste since the business could never use its potential delivered value. This relates to a lean principle calledthe last responsible moment: decisions made too early in a project are hugely risky due to the high level of uncertainty that’s typically at the beginning of this process. 

It is generally a good plan to build and design software following the open/close principle, trying to make it more composable, more decoupled, easier to test and faster to deploy - because economically it opens options. But even this statement may be invalid depending on the context: a product startup need to explore very fast what the market demands, rather than build a consolidated software that will run for the next 10 years. So what could be economically beneficial in one context could be detrimental in others.

When it comes to refactors as investments though, the ROI needs to be assessed, taking into consideration what is the cost, the expected returns and the certainty of the business needs behind. Don't try to do it too early or it will become a straight waste.

Refactors and quality

Sometimes I've heard teams justifying refactors so they could increase the quality of the system. I think it is very important to define what quality means. If we describe quality as "something that meets the requirements", then we can understand how refactors should be considered as a strategic move to be able to keep meeting the requirements today and in the future by:

  • removing constraints (technical debt)
  • investing in generating more options at the right time

A refactor shouldn't be proposed for the sake of it - it has to be economically driven. Quality doesn't mean "perfection", but rather "something that meets the requirements". Our goal as software engineers should be to deliver the exact requirements on time, with operational guarantees, whilst generating enough options to keep evolving the platform based on the information we learn about the business as we go.

You should not write the same code or apply the same development techniques when you're in an exploratory phase business (like an early-stage startup) than when you're re-building a platform that has been running for 10 years, whose level of maturity is way different. The economics are not the same!