Rewrite or Refactor?
Editorial Note: I originally wrote this post for the NDepend blog. You can find the original here, at their site. While you’re there, take a look at some of the other posts and announcements.
I’ve trod this path before in various incarnations, and I’ll do it again today. After all, I can think of few topics in software development that draw as much debate as this one. “We’ve got this app, and we want to know if we should refactor it or rewrite it.”
For what it’s worth, I answer this question for a living. And I don’t mean that in the general sense that anyone in software must ponder the question. I mean that CIOs, dev managers and boards of directors literally pay me to help them figure out whether to rewrite, retire, refactor, or rework an application. I go in, gather evidence, mine the data and state my case about the recommended fate for the app.
Because of this vocation and because of my writing, people often ask my opinion on this topic. Today, I yet again answer such a question. “How do I know when to rewrite an app instead of just refactoring it?” I’ll answer. Sort of. But, before I do, let’s briefly revisit some of my past opinions.
Getting the Terminology Right
Right now, you’re envisioning a binary decision about the fate of an application. It’s old, tired, clunky and perhaps embarrassing. Should you scrap it, write it off, and start over? Or, should you power through, molding it into something more clean, modern, and adaptable. Fine. But, let’s first consider that the latter option does not constitute a “refactoring.”
A while back, I wrote a post called “Refactoring is a Development Technique, Not a Project.” You can read the argument in its entirety, but I’ll summarize briefly here. To “refactor” code is to restructure it without altering external behavior. For instance, to take a large method and extract some of its code into another method. But when you use “refactor” as an alternative to “rewrite,” you mean something else.
Let’s say that you have some kind of creaky old Webforms application with giant hunks of gnarly code and logic binding the GUI right to the database. Worse yet, you’ve taken a dependency on some defunct payment processing library that prevents you from updating beyond .NET 2.0. When you look at this and say, “should I refactor or rewrite,” you’re not saying “should I move code around inside this application or rewrite it?” Rather, you’re saying, “should I give this thing a face lift or rewrite it?”
So let’s chase some precision in terms here. Refactoring happens on an ongoing and relatively minor basis. If you undertake something that constitutes a project, you’re changing the software. You’re altering the way it interacts with the database, swapping out a dependency, updating your code to a new version of the framework, etc. So from here forward, let’s call that a reworking of the application.
To Rework or Rewrite
With that out of the way, we can examine the question in clearer terms. To do that, I will first recall another piece I wrote — one that became fairly popular (and slightly controversial). I called that post, “The Myth of the Software Rewrite.”
In that post I offered a relatively narrow thesis. I made the case that organizations should not undertake a rewrite because the developers piled up too much technical debt to continue effectively. When developers clamor for a rewrite to escape a mess of their creation, you have an organizational smell. Rewarded with a blank slate, the same people will promptly recreate the same problem that landed them where they are. Conceding to this rewrite today almost certainly means conceding to subsequent ones at regular intervals. A team in this situation needs to bite the bullet and learn how to clean up and evolve software.
But that covers only a narrow case. Specifically, I mean the case where the main impetus for a proposed rewrite is the team saying, “we’ve made a mess and need to start over.” What about situations where either the team doesn’t push for the rewrite or else the team pushes for another reason?
Reasons to consider a rewrite abound. Perhaps the software runs on dated hardware or perhaps it has outsize dependencies on dying software. It could be that new feature requests are radically incompatible with the existing architecture or that the way it interacts with other systems has evolved. It might even be that the software is simply a mess, but your team inherited it rather than being responsible for it. I addressed none of these situations with my previous post. And yet, they all merit consideration.
I can offer a relatively simple answer in all cases. In fact, it’s the same basic premise that I use with my clients. You should rewrite when reworking would cost more than starting from scratch. And you should only do either one once you’ve demonstrated a value proposition for change.
The Business Case
Okay, so I’ve offered an overarching bit of wisdom but without much detail. Perhaps you’d even consider what I said somewhat glib. But you have to understand the importance of this central consideration. The decision to rework or rewrite is a business decision based on dollars and cents. Your task now is to make the case for each.
How to build that case would fill at least another entire post. But you can get to a decent approximation with a fairly simple exercise. Take the desired end state of the system, and construct a sequential, architectural roadmap of how you might get there from current state.
First of all, if you can’t do that in the first place, then you should probably consider a rewrite. It might be that no clear path exists from some COBOL, green-screen database application to Angry Birds 2 for iPhone. In that case, just write Angry Birds 2.
If you think you can get there, lay out the steps required for the rework and the components involved in a rewrite. Then, do a grossly high-level exercise in the agile concept of relative sizing. In other words, don’t figure out how many man-hours all of these tasks require. That will take forever, and you’ll just be wrong. Instead, compare sizes. If “build a data access layer” is a 3, then is “decouple all GUI dependencies” more like another 3 or like a 5? You don’t need to think about time — just relative complexity.
This exercise can give you at least an early sanity check. When you build the actual case to bring to the business, you’ll want to factor in other concerns. These might include relative outage times, team morale, licensing costs, etc. And you’ll naturally want to dive deep on some of the estimates, using metrics like coupling and fan-in to see how difficult separation may prove.
But to start, lay out your path and do a bit of sizing. The “rework or rewrite” debate can drag on endlessly and cause a great deal of hand-wringing. Get out in front if it. Chart your course, do some relative sizing, and see how it looks.
What is seldom considered is the soft costs of failing to maintain software. I have no interest in working in VB6… yet in 2009 I was hired to do just that under the illusion we’d be rewriting a system. Then someone who has never written software before made the argument that you shouldn’t rewrite software based on some web article they read. Needless to say, our entire team ended up quitting. Keeping this old cruft around is fine if it never needs to be touched, but if you want to keep enhancing it you need developers, and good quality developers… Read more »
For what it’s worth, I actually factor in the considerations you mention when working with clients in a management consulting capacity. Attracting and keeping talent becomes significantly harder with legacy codebases and messes in general, so I encourage them to factor that into their decision making. (Depending on the people I’m talking to, this can wind up turning into a sulk-fest about how developers are too picky or something… the vestiges of dated “you should be happy just to have a job” thinking)
There is always a chance that the “talent” you are paying is just unexperienced developers with the latest fashionable items on their CVs, or developers who simply fancy leaning the latest fashionable tools. Whatever you develop today, in10 years time won’t be fashionable, as the fashion change. Unless you are proposing to rewrite software every 5 years, you are going to have a “legacy codebase”. What you need to ensure is that in 10 years time you will be able to find, at reasonable price, developers, who would be able to continue support and extend your application. So the advice… Read more »
The question is – how do you know that after the rewrite the result will be better?
Yes, developers, especially young once who never worked on new software before, would love to rewrite whatever they are dealing with. But it does not meant that this rewrite is in company’s interests.
“Developers stagnating skills” is not an argument.
Incidentally, just today, we came across a chunk of code that was incomprehensible and had no tests. My initial approach was to write some characterization tests to capture the existing behavior to figure out what the hell it was doing. However it took us a long time to even write tests and capture collaborators’ outputs and then mock those to even finish testing. The cyclomatic complexity of this code was deplorable. So we switched to plan B. As in dig deep inside the org to figure out why the code was written to begin with. We went back to the… Read more »
Off the cuff, that sounds to me like a good decision. Figuring out what that code is doing and why seems like more work than replacing it.
It is not exactly a system rewrite. Replacing a chunk of code is refactoring. And the approach you have chosen was correct – you need to understand why the code does what it does, not just try to replicate what it seems to be doing.
Interesting read, very applicable to my current situation. Also, Ctrl-F “outsize”, I expect that was a word you were not planning to use.
“Outsize dependencies on…” meaning “extremely large dependencies on…” Does that not sound right?
I respect the specific choice to define the word “refactor”. In 30 years as a software engineer, I’ve learned to mistrust any request to refactor because managers, architects and developers just use the word to mean whatever it is they want it to mean. And rarely does their meaning align with yours, Eric. But it should. Good article.
Thanks! And yeah, I’d say that an external request for refactoring is a smell unless a team member floated it first. With the strict definition, it makes absolutely no sense for an outside party to say, “can you please spend time changing internal stuff that has absolutely no effect on any inputs and outputs?”
I also mistrust this when it comes from inside the team – very often it just means “make it look according to my personal preferences”.
I’m surprised you haven’t really discussed the faults/CR database in this article. Every project will have one, even it is just a simple checklist that the boss has somewhere. The faults/CR database is a fairly good indicator of how much ‘rework’ is required to get the software back to ‘adequate’. In the past I’ve applied a tariff off 1 or 2 days per fault/CR as a rough average which gives a benchmark against any proposed re-write. Measuring the re-write is a bit trickier because nearly always there’s no spec/requirements doc to work from or even a features list. In any… Read more »
You definitely raise some good points here. In truth, it’s a hard subject to speak about purely in generalities. There are the considerations that you’d mentioned as well as some other commenters’ besides. With my own practice, I tend not to actually execute on the app dev contracts if I’m the one making strategic recommendations. In other words, I try to avoid things like, “you should rewrite this, and it just so happens that I can offer you that service.” If I’m called in to assess, I work through just that, and then offer referrals/recommendations for the app dev work,… Read more »
Lol. The 1-2 days figure is really just a number that I’ve pulled from my own butt. No science behind it, just my nearly 30 years experience as a developer. But those are actual days, not relative. As you suggest, estimates are usually worthless and relative estimates don’t really change that fact IMO. Individually taking a 1-2 day estimate for a fault/CRS might not be accurate, but taken over an average of 30, 50, 100, 200 … faults/CRs I think serves as a rough starting point for the scope of work latent in a fault/CR database. However, I do think… Read more »
The lack of real, definitive studies on such topics is, to me, one of the most maddening things about our industry. So much prevailing industry wisdom relies purely on the anecdotal.
I wonder if that will change in the coming years as companies get less and less worried about jealously guarding their source code, processes, and practices.