In the simplest terms, refactoring is restructuring existing code to improve the design, structure, and/or implementation of the software without changing its external behavior. There’s huge value doing it at the right time and for the right reasons, but from an outside perspective, it can seem a lot like redoing — like doubling back and covering the same ground. (Re-anything doesn’t sound great…)
It’s our job as your development team to describe why we might recommend refactoring, and make it make business sense. We’ve found that metaphors can help. Let’s look at one, and then do a few thought experiments together.
An easy way to think about refactoring: the house
One great metaphor for thinking through a decision to refactor comes from the team at the Apollo Division of ACTUM Digital. Imagine there’s a house. It’s a great house with amazing features, totally up to code. The owner loves it, and simply wants to add a rain barrel to the attic to harvest rainwater. The problem? The attic can’t support the weight of a rain barrel.
The homeowner has a few options:
Option 1: Upgrade and future-proof
Upgrading and future-proofing this house would require reinforcing the attic ceiling with metal framing — an expensive solution that will take a couple of months or so to install. The upside is that the new frame won’t change the appearance of the house or its usability; it will just allow the attic to hold this rain barrel now, and as many rain barrels as the owner might want in the future.
This is the equivalent of refactoring, and a lot like the early work we did with Blueprint Registry. Blueprint had a roadmap of features they wanted to implement, but the existing code base would only successfully support about a third of them. Refactoring was the only way to get them all the way where they wanted to go.
Option 2: Do nothing
The next option the homeowner can choose is to not get a rain barrel at all. This is the equivalent of doing no new development on a software project.
It’s certainly the cheapest approach. It costs no dollars and it’s already “done.” That said, there are likely some business or opportunity costs involved in not pursuing the new feature. For Blueprint Registry, they might fall behind their competitors and miss out on market share. In our house example, the costs could be as large as continuing to use groundwater during a drought and risk running out of water; or it might be as nominal as continuing to pay the public utility $100 a month.
Option 3: Compromise to meet the immediate need
The final option is to choose a faster or cheaper route. For our house, we could add support pillars under the attic to hold the one rain barrel. Great! But the pillars would need to go through the kitchen. Not so great.
We love how perfectly Apollo Division put this tradeoff — we can almost hear the meeting with the homeowner: “The only problem is that the pillar will have to stand in the kitchen. If we add more barrels over time, we’ll also install more pillars.” Maybe the homeowner can live with one kitchen pillar, but additional pillars will eventually make it impossible to open the oven doors, stand by the sink to do the dishes, and overall will make living in the house less convenient.
Option 3 is very tempting during a development project. It can be hard to imagine the web app equivalent of the oven door not being able to open. One of our most important jobs is authentically articulating the pain points of a “cheaper + faster” decision. Our goal is always to move from jargon into concrete language that’s as obvious as the oven door example.
Refactoring thought experiment: the homeowner’s association
Our house example focused on refactoring to pave the way for a new feature — a nice-to-have that’s may not necessarily be required. Sometimes, though, refactoring is mandatory. The decision changes from if to when.
Let’s imagine our homeowner chose Option 1: to refactor their house. They have a great rain barrel in a reinforced attic, but now the homeowner’s association has passed a regulation that all the homes in the neighborhood must use electric power. The house is currently using oil. As the homeowner, you can choose to update the house now, or you can pay a fine to keep it as is — $50 the first year, $250 the second to fourth years, and $1,500 the fifth year and beyond.
In a development project, the house is the existing software. Installing the new electric HVAC is refactoring. And the fines, taxes, and maintenance costs you might incur to meet the homeowner’s association’s requirements add to your technical debt.
Would you update the house? And when? Ultimately, there are a lot of factors that weigh into this decision: the budget, the timeline, the amount of time the homeowner is expected to stay in the house. There’s not always one right answer — sometimes it makes sense to delay and take on some technical debt.
One of the websites we were working on redesigning faced this same quandary. The website needed to be ADA compliant, but our new version wasn’t going to be delivered for another 9 months. On one hand, ADA implementation needed to happen now; on the other hand, we knew it would be throwaway work in less than a year. We ultimately recommended a hybrid approach: implement some of the most critical improvements within the existing framework, but don’t refactor the entire front-end.
Refactoring thought experiment: the garden
Let’s do one more example. Often, the need to refactor comes simply from the normal wear-and-tear on your existing code base. So, the house is great, the rain barrel system is awesome, the HVAC system is installed, and the homeowner’s association is satisfied. Now go outside to the garden.
Some plants are doing well — they’re actually overgrown and bushy! — while others are starting to wilt. No one did anything wrong in the garden; it’s just the nature of plants. The next step is to trim the overgrowth and replant the wilted plants in an area with better light. This is refactoring, too. It’s not unlike going back and refining bloated code, or finessing a feature that had been bandaid-ed together to meet a fast turnaround.
It’s not unlike framework updates. Just recently, we worked on a microservice-based project that was built on Nodejs version 10. When we were asked to build new microservices for that architecture, we had to point out that Node 10 was at the end of life and not supported by AWS. We would actually need to refactor all the existing microservices to version 14, and then build the new ones.
Figuring out the right frequency of maintenance versus letting the “plants” just do their thing can be tough. It’s important to regularly perform at least some maintenance and optimization to keep the project running smoothly. We’d recommend allowing a project to seriously deteriorate — either by wilting or overgrowth — if you’re going to sunset it, or if the cost to refactor outweighs the usefulness of the project. For example, if next year our homeowner is going to re-landscape the entire property and add a pool, there’s no need to prune back much of anything right now.
Our POV: Refactoring is ultimately a business decision
When our clients are facing the refactoring decision, we try to flip the question around and ask: What is the cost of not refactoring? We compare that to the costs of the refactor, including time and effort, the savings after the refactor, and the benefits of the refactor: new flexibility, reduced bloat and complexity, easier to fix bugs and maintain code. (Don’t forget the payback period, too.)