To Rewrite or Refactor?

Shakespeare for Software Engineers (sorta)

Run-through

AI-Powered Chatbots in Customer Service and Engagement

Using AI for customer service in your company is a definite method to save time and money. If you’re like most business owners, you’re constantly searching for fresh, creative ways to improve your enterprise. We’re here to inform you that improving AI customer service is a simple and rapid win.

Read More »

To rewrite or refactor, that is the question. We know it from Shakespeare’s Hamlet, Act Three, Scene One – the contemplation of life, death, and pain. It’s a complex matter, and it’s not always the software engineer’s decision to make. Many organizations don’t have the budget to support refactoring or rewriting to improve or update their legacy systems. Hospitals and health care providers have been notorious for this. But there are all kinds of situations where engineering managers can choose or at least influence the choice. Let’s try to simplify that decision.

What is the difference between refactoring and rewriting?

Refactoring and rewriting code are two processes for improving poor quality code to make it easier to read and add new functionality.

Refactoring

Refactoring is the process of altering software in a way that doesn’t change its external behavior. This process typically means numerous minor code changes albeit with potentially significant structural adjustments. Refactoring serves to reduce code complexity, improve code quality, decrease (and make it easier to find) defects. With each segment of code being tested before moving onto the next, dealing with hundreds of thousands Lines of Code may make it impractical.

Rewriting

Rewriting entails tossing out the old code and writing it anew, inherently altering the code’s behavior. As the underlying logic and requirements are at least generally established, rewriting code is arguably faster than writing it from scratch – and sometimes faster than refactoring. However, rewriting code can introduce new bugs and vulnerabilities. However, Rewriting is resource intensive in that while you’re creating the new code, you still have to maintain the old code.

Generally, if code is working and bugs are minimal, regardless how ugly it may be, it may be best to just leave it alone. If Microsoft can get away with Blue Screens of Death for 3+ decades, unless your software has a security vulnerability that could be used to start WWIII, you’ll probably be okay. Probably. We’re going to presume that the code is perhaps partially working and bugs are significant – mandating some kind of action.

It’s also worth noting that refactoring and rewriting are not mutually exclusive. It may be possible to refactor one component, while another component may require a rewrite. So, it’s worth evaluating the software with your team and supporting analytics (code complexity, test coverage, and defect rates) to form a plan.

Reasons to Refactor or Rewrite Code

The code has issues. It can be a headache to read, a nightmare to understand, and a PITA to modify. It can be impossible to write useful tests for, may be riddled with defects and bugs, have security vulnerabilities, and performance issues. More than this, the way the code was written makes it difficult to add new functionality. Ultimately, the code is negatively impacting (existing and future) value while posing a risk for added costs (inefficiency and turnover, for starters).

It’s useful to remember that software has an average lifespan of 7 years, but that can extend to 12+ years if it has over a million lines of code. Concurrently, software developers tend to change jobs every 1.5 to 2 years. A given software project may see 3 to 6 complete rotations. Each new member to the team may require 3 or more months to get up to full speed with the code. Developer skill, team structure, and skipping requirements to meet deadlines all contribute to technical debt.

A lot of other things can change over time, too – new operating systems, devices, third-party APIs, use of programming languages, emergence of new technologies, software regulations (like GDPR), and of course, the competition. These are examples of inevitable technical debt.

Good Times to Refactor

Refactoring should be a regular part of your team’s routine. When it is, the constant incremental effort will help prevent accumulating large amounts of technical debt. There will be times where your team has no time to refactor. You could also inherit a codebase where the previous team didn’t refactor regularly.

  • Before adding new features or functions.
  • To simplify code with a high Cyclomatic Complexity and/or low test coverage which is likely to have high defect rates (unless Cognitive Complexity is low).
  • When the original coder is still on the team.
  • If a developer gives notice, consider evaluating their code to identify if there’s anything they should refactor before they depart.
  • When you simply don’t have the resources to perform a rewrite – to manage both old and new versions simultaneously.

Perhaps the best overall guidance is to always refactor – unless there’s a good basis for a rewrite. This bring us to…

Good Times for Rewriting Code

There will be times that you inherit code that’s so messed up it will take you longer to do enough refactoring to be able to meaningfully do anything with it. The original coders are long gone, documentation and comments may be non-existent. The whole thing, or perhaps just parts of it push your team into a state of WTF?

Firstly, a rewrite is only viable if you have sufficient resources to divide efforts between maintaining the existing code AND writing the new code.
Secondly, rewrites can go horribly wrong if the effort isn’t properly assessed and scoped. Wherever possible, it’s best to limit the scope of the rewrite, otherwise it can take years.
A rewrite may be in order if the software or portions of it use a programming language that’s in steady decline. This can make it harder to find future developers (examples: Pascal, Haskell, Erlang, CoffeeScript).
Rewrites are necessary when upgrading features to remain competitive in the market.
You’ll need to rewrite when components are so closely coupled together that a change in one system necessitates changes in many/all systems, so refactoring is not a viable option.

It would be a crime to not also reference the refactor and rewrite discussion on StackExchange, even if it is a bit dated. It covers detailed pros and cons of both options, but one of its gold nuggets simplifies the decision:

The cost of rewriting the application + maintaining the rewritten application is less than the cost of maintaining the current system over time.

Gitential’s Metrics for Refactoring and Rewriting

Gitential uses three primary metrics for tracking refactors and rewrites to help you monitor the effort and impact on your project. You can also use this data to plan refactoring and rewriting efforts into your development schedule.

Legacy Refactor Total LOC

With this heuristic, if the ratio of new vs. deleted code in a single patch or commit is high enough, it’s tracked as a rewrite. If the ratio of inserted code is roughly equal to deleted code or less, then it’s tracked as a refactor – unless we detect a “bug” string in the commit message (then it’s tracked as a bug fix).

Hours Spent on Rewrites

This metric covers total estimated coding hours spent on rewriting code (churn) that was originally written within the last 3 weeks.

Waste Throughput (%)

The percentage of waste out of all implemented work.

These can be compared against three additional key metrics…

Code Complexity, Test Coverage, and Defect Rates

Code that has low test coverage is generally indicative of high code complexity and is prone to high defect rates. These are all direct indications that code needs to be refactored or rewritten. Halstead’s metrics can be used to assess the extent to which code can be simplified and the theoretical impact on reducing defects. Refactoring probably is not necessary if the code has low Cognitive and low defect rates even if it has high Cyclomatic Code Complexity.

Hidden Costs of Not Refactoring/Rewriting

Most software is produced to make money, so all software development efforts must be weighed – first against the budget, and second for ROI. For many clients, this typically means constantly creating value by adding new features or improving UI/UX to increase user engagement. Revenue related issues are one factor.

Clients also understand that software developer wages are the #1 expense in software development. However, it’s usually only the engineering manager who understands that other factors can influence cost. Ugly complicated code decreases efficiency and productivity, slowing down the creation of new value across the software’s entire lifespan.

Poor code quality doesn’t prompt developers to find another job – but not being able to fix it will. Turnover incurs replacement costs of up to 30% of their salary and adds 3+ months of inefficiency for the new hire. And they, too, may quit. Concurrently, there’s a global shortage of software developers, so it’s up to the engineering manager to optimize their skills, allocation, and morale.

Discussing Refactoring and Rewrites with Clients

Are you on a project with a gnarly code base and a client that’s only interested in revenue? Assuredly, they’re aware of the costs of development, but they may not be aware of how their fubar code is increasing costs and making it harder to bring in new revenue. If so, you may need to enlighten them.

It varies on your staffing model, but replacing in-house developers typically costs 30% of the developer’s base salary. The average US developer makes about $110k, so one developer quitting for not being allowed to fix the code would run ~$36k. If the average lifetime value of a customer is $100, that washes out the revenue from 360 customers, straight-off. Add Cost of Acquisition (should be a third or less of LTV), and it jumps to 480 before we even start on the impact of inefficiency and delayed features.

Contending with Legacy Systems

Refactoring and rewriting code for legacy systems can be a mammoth undertaking. Many organizations spend 80-90% of their IT budgets maintaining legacy systems, but they’re likely hindering revenue growth on top of that. The situation didn’t get there overnight and change is probably being “held up.” When refactors and rewrites are no longer viable, your next best option is to look at leapfrogging technologies.

The following episode of CXOTalk interviews David Chou, VP, CIO, and CDO of Children’s Mercy Hospital in Missouri, a $2 billion pediatric care organization with 8,000 employees. Though a C-level discussion, from 4:00-22:00 he highlights many of the bigger issues that go hand-in-hand with digital transformation (and the upgrading or replacing of legacy systems).

A quick summary of the discussion points:

  • Perpetuated by risk-averse senior staff: “It’s always been done this way.”
  • Look at leapfrogging technologies
  • Need bottom-up and top-down buy-in
  • More training for end-users

Did you like our content?

Spread the word

Subscribe to Our Newsletter

Don't miss our latest updates.
All About Software Engineering Best Practices, Productivity Measurement, Performance Analytics, Software Team Management and more.

Did you like our content?

Spread the word

Subscribe to Our Newsletter

Don't miss our latest updates. All About Software Engineering Best Practices, Productivity Measurement, Performance Analytics, Software Team Management and more.