I believe in refactoring. The Agile Manifesto holds that
The best architectures, requirements and designs emerge from self-organizing teams.
The quality of our software systems depends on refactoring. In fact, I believe that the only way that an organization can avoid refactoring is by going out of business. Maybe I should explain that.
Refactor or Die
Every software system that we build is inside a dynamic environment. The organization(s) using the software are all in a state of constant change. The people using the software are also constantly changing. Due to this constant change, every software system needs to be adapted to the environment in which it is used. Most of the time, businesses think of this constant change in terms of new features and enhancements – the scope of functionality that a system can handle. Less commonly, businesses think of this change in terms of the obvious external qualities and attributes of the system such as performance or security. But almost never does an organization, from a business perspective, think of the invisible qualities of the software system such as simplicity and technical excellence.
What happens when the business does not recognize those invisible qualities? I’m sure almost every software developer reading this can answer this question easily: the system becomes “crufty”, hard to maintain, bug-prone, costly to change, maze-like, complex. Some people refer to this as legacy code or technical debt.
The longer this state is allowed to continue, the more it costs to add new features – the stuff that the business really cares about. It is pretty easy to see how this works – for someone who has a technical background. But for those without a technical background it can be hard to understand. Here is a little analogy to help out.
Imagine that you set up a system for giving allowance to your kids. In this system, every week your kids have to fill out a simple form that has their name, the amount that they are requesting, and their signature. After a few weeks of doing this, you realize that it would be helpful to have the date on the form. You do this so that you can enter their allowance payments in your personal bookkeeping records. Then you decide that you need to add a spot for you to counter-sign so that the paper becomes a legal record of the allowance payment. Then your kids want extra allowance for a special outing. So you add some things on the form to allow them to make these special requests. Your accountant tells you that some portions of your kids allowance might be good to track for tax purposes. So, the form gets expanded to have fields for the several different possible uses that are beneficial to your taxes. Your form is getting quite complex by this point. Your kids start making other requests like to be paid by cheque or direct-deposit instead of in cash or to be paid advances against future allowances. Every new situation adds complexity to the form. The form expands over multiple pages. Filling out the form weekly starts to take significant time for each child and for you to review them. You realize that in numerous places on the form it would be more efficient to ask for information in a different way, but you’re not sure if it will have tax implications, so you decide not to make the changes… yet. You decide you need your own checklist to make sure that the forms are being filled out correctly. A new tax law means that you could claim some refunds if you have some additional information… and it can be applied retroactively, so you ask your kids to help transcribe all the old versions of the form into the latest version. It takes three days, and there is lots of guess-work. Your allowance tracking forms have become a bureaucratic nightmare.
The forms and their handling is what software developers have to deal with on a daily basis – and the business usually doesn’t give time to do that simplification step. The difference is that in software development there are tools, techniques and skills that allow your developers to maintain a system so that it doesn’t get into that nightmare state.
For a more in-deth description of this process of systems gradually becoming more and more difficult to improve, please see these two excellent articles by Kane Mar:
Technical Debt and Design Death
Technical Debt and Design Death: Part II
Ultimately, a software system can become so crufty that it costs more to add features than the business benefit of adding those features. If the business has the capacity, it is usually at this point that the business makes a hard decision: let’s re-write the system from scratch.
I used the word “decision” in that last sentence. What are the other options in that decision? Ignoring the problem might be okay for a while longer: if the company is still getting benefit from the operation of the system, then this can go on for quite a while. Throwing more bodies at the system can seem to help for a bit, but there are rapidly diminishing returns on that approach (see The Mythical Man-Month for details). At some point, however, another threshold is reached: the cost of maintaining the operation of the system grows to the point where it is more expensive than the operational value of the system. Again, the business can make a hard decision, but it is in a worse place to do so: to replace the system (either by re-writing or buying a packaged solution), but without the operating margin to fund the replacement.
In his articles, Kane Mar describes this like so:
It’s pretty clear that a company in this situation has some difficult decisions ahead. There may be some temporary solution that would allow [a company] to use the existing system while building a new product, [A company] may decide to borrow money to fund the rewrite, or [a company] may want to consider returning any remaining value to their shareholders.
In other words, refactor or die.
Refactoring and Business
In the Scrum Master and Product Owner classes that we teach, this topic comes up frequently: how does the business account for refactoring? How do we “govern” it? How do we make good decisions about refactoring?
There are a few principles that are important in helping to answer these questions. All of these principles assume that we are talking about refactoring in an Agile team using a framework like Scrum, OpenAgile, or Kanban.
Refactoring Principle One: Keep It Small
Refactoring is safest and cheapest when it is done in many small increments rather than in large batches. The worst extreme is the complete system re-write refactoring. The best refactoring activities take seconds or minutes to execute. Small refactorings create a constant modest “overhead” in the work of the team. This overhead then becomes a natural part of the pace of the team.
Not all refactoring moves can be kept so small. For example, upgrading a component or module from a third party might show that your system has many dependencies on that module. In this case, efforts should be made to allow your system to use both the old and the new versions of the component simultaneously. This allows your system to be partially refactored. In other words, to break a large refactoring into many small refactorings. This, in turn, may force you to refactor your system to be more modular in its dependencies.
Another common problem with keeping refactorings small is the re-write problem. Your own system may have a major component that needs to be re-written. Again, finding creative technical means to allow for incremental refactoring of the component is crucial. This can often mean having temporary structures in your system to allow for the old and new parts to work harmoniously. One system that I was working on had to have two separate database platforms with some shared data in order to enable this “bi-modal” operation.
Refactoring Principle Two: Business Catalysts
When is the earliest that a refactoring should be done? Not whenever the technical team wants to do it. Instead, the technical team needs to use business requests as catalysts for refactoring. If the business needs a new feature, then refactoring should only be done on those parts of the system that are required to enable that feature. In other words, don’t refactor the whole user interface, just refactor the parts that relate to the specific business request.
Again, there can be exceptions to doing this… but only in the sense that some refactorings might be delayed until a later date. This is tricky: we want to make sure that we are not accumulating technical debt or creating legacy code. So, instead, we need to allow the technical team to refactor when they detect duplication. Duplication of code, data or structure in the system. A business request might impact a particular part of the system and the team sees how it might be necessary to refactor a large swath of the system as a result. But, the cost of doing so is not yet justified: the single request is not enough of a catalyst, and the team can also choose a simple temporary solution. Later, the business makes another request that also implies the same large refactoring. Now is the time to seriously consider it. It is now a question of duplication of another simple temporary solution. The business may not be happy with the extra expense of the large refactoring so the principle of keeping it small still applies. However, the technical team must also be willing to push back to the business under the right circumstances.
Refactoring Principle Three: Team Cohesion
Teamwork in Agile requires high levels of communication and collaboration. In refactoring work, teamwork applies just as much as in any other activity. Here, it is critical that all members of the team have a unified understanding of the principles and purpose of refactoring. But that is just the first level of team cohesion around refactoring.
The next level of team cohesion comes in the tools, techniques and practices that a team uses in refactoring. Examples include the unit testing frameworks, the mocking frameworks, the automation provided by development tools, continuous integration, and perhaps most importantly, the team working agreements about standard objectives of refactoring. This last idea is best expressed by the concept of refactoring to patterns.
The highest level of team cohesion in refactoring comes from collective code ownership and trust. Usually, this is built from practices such as pair programming or mob programming. These practices create deep levels of shared understanding among team members. This shared understanding leads to self-organizing behaviour in which team members make independent decisions that they know the other team members will support. It also impacts research and learning processes so that teams can do experiments and try alternatives quickly. All of which leads to the ability to do refactoring, large and small, quickly and without fear.
Refactoring Principle Four: Transparency
In many ways, this is the simplest refactoring principle: the team needs to be completely open and honest with all stakeholders about the cost of refactoring. This can be difficult at first. Another analogy helps to see the value of this. A surgeon does not hide the fact that care is put into creating a clean operating environment: washing hands, sterilizing instruments, wearing face masks and hair covers, restricted spaces, etc. In fact, all of those things contribute to the cost of surgery. A surgeon is a professional who has solid reasons for doing all those things and is open about the need for them. Likewise, software professionals need to be open about the costs of refactoring. This comes back to the main point of the first part of this article: hidden and deferred costs will still need to be paid… but with interest. Software professionals are up-front about the costs because doing so both minimizes the costs and gives stakeholders important information to make decisions.
The challenge for business stakeholders is to accept the costs. Respecting the team and trusting their decisions can sometimes be very hard. Teams sometimes make mistakes too, which complicates trust-building. The business stakeholders (for example, the Product Owner), must allow the team freedom to do refactoring. Ideally, it is continuous, small, and low-level. But once in a while, a team will have to do a large refactoring. How do you know if the cost is legitimate? Unfortunately, as a non-technical stakeholder, you can’t know with certainty. However, there are a few factors that can help you understand the cost and it’s legitimacy, namely, the principles that are described here.
If the refactoring is small, it is more likely to be legitimate.
If the refactoring is in response to a business catalyst, it is more likely to be legitimate.
If the refactoring is reflective of team cohesion, it is more likely to be legitimate.
And, of course, if the refactoring is made transparent, it is more likely to be legitimate.
4 thoughts on “Refactoring: 4 Key Principles”
I’ve encountered an environment where the ticker/branch per feature process is a disincentive to refactoring. I am hoping to find some specific advice on how to manage this in such a process. I’ve been turning teams’ productivity around by refactoring legacy systems my whole career. But the way organizations handle branching and testing these days makes it so difficult.
Now in the Awesome List of resources on Agile Software Development is under way: https://github.com/lorabv/awesome-agile-software-development/Refactoring.md
Add your favourites and/or let me know if there is a topic you would like this list to include.
Thanks for sharing this article.This gives me a lot of ideas about refactoring. This article is really helpful especially for business owner like me.
Do you have a rule of thumb for the amount of refactoring to expect? Ideally in a percentage of the entire effort. Is there any literature that covers this? I have heard 20% from several experienced scrum masters and project managers. This is confirmed my own experience and I am therefore tempted to go with that number. Still I wouldn’t mind knowing if that is widely considered to be plausible, probable or even quite efficient and also wouldn’t mind a widely respected source for this opinion.