In a previous post, I tried to provide an analogy to help non-technical people understand why the number of produced lines of code (LOC) is not a good measure of developers’ productivity. While there may be a demand for such an analogy, the demand is probably even higher for an analogy for refactoring. I sometimes view refactoring as the eternal apple of discord between developers and stakeholders (and I am not saying developers are always right to refactor). The need to refactor is not always immediately clear to management or other stakeholders, and, if they have never seen a large codebase from the inside, who could blame them?
Product people might have an ambitious quarterly plan bursting with new feature requirements. Time is pressing. But developers want to spend one entire week or more on work that will, in the ideal case, preserve the existing behaviour and not affect anything negatively. They will add no new functionality, and possibly not even any performance improvements. In many cases, this is a hard sell. Why didn’t they write good code from the start?
In Clean Code, Robert Martin compares refactoring to cleaning up the kitchen after dinner. If you have dinner and you do not do the dishes afterwards, you can do something else sooner (e.g., watch TV or play football). In the short run, you move faster. However, when you prepare the next meal, these dishes will probably be in the way. You can work around that for a while, and put them in a corner where they do not encumber you that much. But if you keep skipping the clean-up after dinner, the pile of dirty dishes will become larger and larger, until it is in the way whatever you try to do in the kitchen. The mess will slow you down.
This is a clear, understandable analogy, but it has its shortcomings when you try to translate it to the software development domain. When you develop software, you build something. When you have dinner, you don’t. Having dinner produces dirty dishes as a result. What do the dirty dishes represent? Maybe rushed code in your application that slows you down and is in the way. What I am missing in the analogy, though, are new features and requirements, new team members that are confused by strange conventions or unwritten laws, and abuse of existing structures for quickly bolting on new functionality. I will try to cover these aspects in my analogy below, and, admittedly, it gets more complicated than the clean-up-after-dinner example. I still thought it would be worth a try, though.
Let’s build a house
Here we go: Imagine a picture-book family (mother, father, daughter, son) building a house. The initial requirement is for the new home to house a family of four, plus to have a garage for a car. So this house is built, along with the desired garage.
Some time later, the father of the family develops a new hobby: Woodworking. There is no free room in the house to set up a workshop, and there are no time and resources to build a shed or something similar next to the house. Therefore, he uses the garage for his hobby. This works, but slows him down a bit, because whenever he wants to do some work, he might have to drive the car out of the garage so he has space. Conversely, the car cannot be parked in the garage unless all tools and works-in-the-making are put aside. It even happens sometimes that the family does not put the car in the garage over night, because it is too cumbersome to put the woodwork aside.
The heating in the house oil-fired. When the house was built, this was a reasonable decision, because the oil price was very low, and likely to remain so. However, the oil price is skyrocketing some time later. Because they own a small forest, the family wants to switch to heating with a modern, central fireplace that produces a nice natural warmth in the entire house. So they get a fireplace installed and start piling up wood. Because there is a lot of wood thievery going on in this area, they store the firewood in the garage.
With the firewood and dad’s workbench occupying garage space, the car does not fit in any more. Because it is the fastest solution, the family rents a vacant garage in the neighbourhood. Obviously, this comes at a monthly cost for the rent, and the distance from the house to the car is a lot longer now, which makes usage more cumbersome and even keeps family members from using the car entirely on some days.
Heating with wood has another effect: The giant oil tanks that take up an entire room on the ground floor are now useless, because there is no oil-fired heating any more. Likewise, the radiators in every room are not needed any more. On the contrary: Sometimes, when the family is out of town for a few days, they let friends stay in their house. These friends are regularly confused by the radiators and try to put them on - to no effect, of course.
The right thing to do would be to remove them, but that would cost money and cause dirt and disarray. So the de-installation of the obsolete heating system is postponed time and again. As it happens, the room with the oil tanks has the perfect size for dad’s wood workshop, which would then free up space for the car in the family’s garage, and make the second garage unnecessary. But getting rid of the tanks is expensive, as well, so the family shies away from the investment.
I could go on, but I think the main elements related to refactoring have already been illustrated:
- Features: Parking space for the car, dad’s workshop, wood-based heating, and storage for firewood represent features.
- The God Class: The garage is an example of how additional functionality gets bolted onto existing structures, e.g., the workshop gets set up in the garage instead of a separate shed. Later, the garage is also used as a storage space for firewood. This is analogous to adding the fourth, fifth, and sixth parameters to an existing constructor to cover new use cases.
- Slowdown: The double usage of the garage leads to a slowdown in dad’s woodworking, because he has to move the car every time he wants to do woodwork.
- Bad practices: Sometimes, the car is not put in the garage because dad is too lazy to put his woodwork aside. This increases the risk of damage through hail or vandalism.
- Inefficiency: When the car is parked in the rented garage, away from the house, the family starts to avoid using the car. They do not use the tools and possibilities they have, because the hurdle to using them is high. In a codebase that has accumulated cruft, there can be such hurdles, too, and they prevent us from doing the right thing.
- Confusion/bugs: When friends stay at the house for a couple of days, they use the heating wrongly. This is like new teammates who are confused by the strange quirks in the codebase, and produce bugs.
- Refactoring: Finally, refactoring the house properly would mean removing the oil tanks, the obsolete radiators, putting the wood workshop in the freed-up room where the tanks were, putting the car back in the adjacent garage, and not renting the second garage any more. That would save money and time, remove inefficiency, and avoid misunderstandings. In a codebase, the same results apply.
Ideally, you would never build a house the way I described above. You would try to plan appropriately. However, you cannot plan everything in advance, because requirements change, and we all want to be agile and deliver in iterations. Houses are changed, even though it is a lot of effort to remove or add walls, bricks, tiles, and so on. Software, on the other hand, is extremely malleable and easy to change. In a fast-paced development environment, an application changes much more rapidly than any real-world construction could ever do. Therefore, waste, cruft, and inefficiency find their way into a codebase much faster. If you fail to refactor, it will slow you down not just in the long run, but indeed very soon.
However, be wise about refactoring. The firewood can stay in the garage, as long as the car still has space. It would probably be overkill to build a shed just for the firewood. In The Effective Engineer, Edmond Lau advises us to be pragmatic about where we invest time, and where our effort can create the most value. This applies also to refactoring.
Maybe you have already been in a situation where you tried to explain to a stakeholder why you needed to refactor something, and it was exhausting. Maybe this situation will come again. In these discussions, describe the benefits that you expect from your refactoring effort, and focus on business value. If they wonder why the need for refactoring arises at all (and: didn’t you refactor just two months ago?), then try Bob Martin’s dishwashing analogy, or refer them to this article. I hope it helps.
This blog post took me about 4.5h to produce. God, I’m slow. A lot of time was spent thinking about alternative analogies. I had a good one about accidentally building a multi-tool that was awkward to use.