
Refactoring is the art of keeping the output and input of the system the same while you make the system or process much smoother. Each software is created for a purpose; some for university projects, automating a boring process, a startup idea, or an enterprise solution. The key differences are their complexity and time-to-live (TTL). Although measuring the complexity of a software project is not too easy to formulate, there are some factors we can consider and explore to have the same perspective in general, like:
- Problem scope
- Number of dependencies involved, either internal or external
- Cost
- Stability requirement
- Type of the data in use
- Security concerns
- etc.
After almost clarifying the complexity, consider the software's TTL as the next step. In a world where we face so many changes every day, having a clear expectation of the software project can help you better design and develop the project.
Software category by TTL and complexity

Short TTL, Low Complexity; These kinds of software have a narrow and well-defined scope and you can find a proofed solution for them easily. They have a short TTL and low complexity is their core property but you might have seen them more. For example, a simple tik-tack-toe game, or a bash script for rotating a single log file as a temporary solution.
Long TTL, Low Complexity; By keeping the complexity the same and increasing the TTL, we have to consider more terms, like technology in use, predict future events and requirement changes, developer experience, and more. Hopefully, the complexity level is still low, and accidental events or mistakes cannot cause a huge problem but it's a good practice for more complex systems.
Short TTL, High Complexity; Think about media-file analytics software, a real-time data analytics dashboard, etc. They are some complex software projects but they might replaced by other tools.
Long TTL, High Complexity; These are the software we probably use each day or act as backing services for our daily interaction in the real or digital world. Unlike case (2), as complexity grows, an inappropriate decision or mistake could cost you a lot. So, you have to manage changes and decisions accurately.
When does refactoring come up for the software?
By the time, you start to write software or develop a new feature, you have to choose your best option as the problem is understood. In the long term, the software growth or the requirements and environment change can cause a new problem or a performance issue to occur and you have to consider new ways or change the worked solution.
Software engineering at Google - Figure 1-1
As you can see in the chart, the importance of upgrading projects jumped when the lifetime goes from a year to decades.
For any project that didn't plan for upgrades from the start, that transition is very painful…
"Software Engineering at Google"
So, it's clear that the refactoring is a part of the system upgrade and it came up front in the long-lived software project, more than a year! Thus, the (2) and (4) cases with the long TTL are the kinds of projects that seem a stage for refactoring! Now we know the place, but why should we refactor??
Why should we refactor?
Every decision has costs and benefits, and you as a software engineer, developer, technical leader, or engineering manager should consider their tradeoff and then participate in making the decision together.
It's noticeable that before refactoring starts, the system should have proper test coverage first.
Here can encounter the benefits of refactoring:
- Increasing the developer's productivity: It's about making the code easier to understand. As far as the code is clear and well-structured, not only you and your team but everyone in the engineering team can easily onboard and contribute. Thus, more efficient communication through codebase questions occurred as every teammate had a solid understanding of the issue.
- Greater ease in identifying bugs: This will split into two parts: tracking down bugs and solving them. By breaking up complex statements into smaller, bite-sized pieces and extracting logic into a new function, you can both build up a better understanding of the code's duty and isolate the bugs.
Although it has benefits, it might bring risks like:
- Serious regressions: By changing a working code, you are at a chance of interruption in system processes, and it will be increased if there is no test! In a fast-growing company, that coding speed is not at the pace of testing, changing the core files is more like walking across a minefield.
- Unearthing dormant bugs: Refactoring helps identify bugs, but it can unintentionally raise undetermined or dormant bugs hidden for a long time. These kinds of bugs are hard to recognize.
- Scope creep: Experiencing immediate and highly significant improvement when making focused, localized changes is incredibly rewarding. By limiting the refactoring to a set of changes, other developers can comfortably review and suggest. Good refactoring keeps the scope solid and prevents side effects as much as possible.
- Unnecessary complexity: The primary goal should be producing human-friendly code, even at the cost of your original design change and it will occur by focusing on the process, not the solution. This way of thinking helps you achieve less complicated software in the end.
After reviewing the pros & cons, you should be aware of an important note:
We must keep in mind our ultimate goal: bettering the code in a way that is clear to both you and future developers interacting with the code
"Refactoring At Scale, by Maude Lemaire"
When NOT to refactor?
- For fun or out of boredom: When you are refactoring for fun, you're not focusing on the impact your change will have on the code, the overall system, and your coworker. You might have different kinds of motivation; testing new patterns, using more far-fetched language features, etc. There is a time and place for testing new things that refactoring is not the place. Refactoring, ideally, is for the smallest changes for the biggest positive impact.
- Because something catches your eye: It's common, especially in large teams, that you face a part of the system and think of a better way in place of the current solution and you are not responsible for it. In such a situation, digging into the refactoring without talking to the owner or having a discussion with the author (in case of availability) is a bad thing, as you probably aren't aware of the scope and the problem. Suggesting and talking to the owners is the best move in these situations.
- To make code more extendable: It might sound a little confusing but as it is unwise try to make the code extendable for the future that hasn't arrived because you don't have a clear understanding of the further requirements and the wins will not be tangible.
- When you don't have time: The half-refactored code is the worse thing than the required code to refactor. When there isn't a specific time the refactoring ends, the reader will have trouble following the refactoring path. It also will block the ongoing features or deprecation plans. By the way, if you plan a refactoring but don't have enough time, try to scope down the changes to make some improvements that comfortably reach the finish line.
When to refactor?
Calculating the tradeoff in refactoring is not as easy as "when the benefits outweigh the risks". Each puzzle piece should affect the decision by the weight we assign to them. It's a good practice to use some evidence in our decision-making process, like:
- Small scope: A small, straightforward section of well-tested is a good place to start refactoring unless you are uncertain about objective improvement or fear affecting a large area.
- Code complexity actively hinders development: Is the development hard? How much do the developers fear of change in the code? Is there in the system that is critical and developers pass it to others? So, the bell rings! To prove it, you can gather the last 6 months' bugs and trace them in the code to consider refactoring or not!
- Shift in product requirements: In other words, these changes mean with the same input we expect other output, and if the current state is not working well, it's an opportunity for you to make the refactoring and then start implementing the new functionalities. To make a long story short, you set a standard of high-quality code, cashing in all the benefits of refactoring, and supporting business objectives.
- Performance: With a deep understanding of the system, you can identify which levers you can use to tilt the scales positively. As this factor speeds up the functionalities, it's part of refactoring. By isolating the levers you identified, refactoring could be done with lower risks.
- Using a new technology: If you opt to replace an existing technology with a new one, you have to plan a deprecation plan by identifying all affected call sites and migrating them. Or if you opt to use a new technology moving forward, you have to identify high-leverage candidates for early adoption with a plan to expand usage. It's recommended before adding new pieces, clean up the mess and tidy the system first.
Conclusion
As said earlier in this article, refactoring is the art. It's not a fast activity that can decide and roll back easily. By considering all aspects of the system, team members, and business you can participate or trigger the refactoring process. Here is a funny but real catch up :)

Hope you enjoy the article!
References:
- Software Engineering at Google by Titus Winters, Tom Manshreck & Hyrum Wright - O'Reilly publication
- Refactoring at Scale by Maude Lemaire - O'Reilly publication
- https://www.iseoblue.com/post/understanding-the-project-complexity-matrix-for-effective-project-management