Refactoring/Rewriting

Started by
4 comments, last by marcusz 17 years, 10 months ago
I'm writing a graphics engine, and I just decided to make it completely OpenGL, even though I've been attempting to write it with API independence in mind (even though I haven't written a line of DX code). It's recently been getting harder to add new things, and the existing code feels a little awkward, not to use, but to edit. How do you know that you're at the point where you should throw away some code and refactor/rewrite most things? I mean, where you're effectively at the point when re-writing everything would make for less work in the future than adding the same features now?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Advertisement
The main requirement for me when deciding to rewrite something is "Have I learned enough that I can avoid the major mistake I made in the previous design?"
If you can't pick something significant that you would change, then rewriting code is wothless because you'll get just as far as you are now with the same problems after a lot of wasted time.
If, on the other hand, you know at least one major change that you would make, you have a chance to actually improve things and it might be worth it to start rewriting right now (you might not make real progress the next rewrite, because maybe you were mistaken, but at least you eliminated one of the possibilities =-)
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
I have a lot of thoughts on the subject.

First, refactoring is *much* different than a rewrite. Refactoring is a carefully controlled process of changing the structure of the code. Generally refactoring does not involve major changes to the source code, just basic tasks like turning a block of code into a function, promoting functions up the class hierarchy, and so on. OTOH, rewriting code generally means you look at the existing code as a reference, yank it out of the source tree, and rebuild that chunk from scratch. Rewriting code means throwing away existing codified knowledge in the hope that you can do better the second time around. It is generally not a controlled process and is more risky to the program.

In addition to what Extrarius said, there are some other issues to consider:

* Version Control -- It is shocking to me how some people trust to luck rather than use a version control tool so they can undo their changes if things go bad.
* Red/Green/Refactor -- Use a test framework to make sure you don't break other parts of the system.
* Have a clear goal, follow through -- It has been said that computer programming is the art of doing one thing at a time. Pick one task and follow it through to completion.


What is your goal? Are you trying to make it independant of API, or are you trying to make it easier to edit? Pick one task and see it through to completion. Then work on the other. If you have to change tasks in the middle, use your version control to tag the changes you have been making, undo them, make the code easier to edit, and then re-apply the changes using your new knowledge.

I never personally throw away code. It's always still available to me in version control. How do I know when to rewrite? I refactor all the time since I practice TDD (test driven development). Refactoring doesn't mean throwing out the code. It means restructuring the code that's already in place to make it easier to understand and maintain.

Your last question asks about times that a rewrite has made sense. In my experience, it generally means that the section of code takes at least twice as long to design and write. If the piece of code took 50 hours to write the first time, you should carefully design the new version by studing the old one and making improvements -- and it will take about 100 hours to complete.

One recent case was a project that took six work-years to initially develop. The changes are just recently in production. The total rewrite took 14 work-years to complete. The change in that particular case was worthwhile because the existing code base was core to the business but was reaching the point where changes took too much time and the morale was suffering. The changes could have been prevented if the initial project had been carefully thought out in advance. The complete lack of design and the hodge-podge development practice of the initial developers could not be overcome through refactoring. We decided to rewrite instead of refactor after we spent about one work-year trying to refactor the code base, but could not untangle the mess. The final result was radically different than the original, and changes from it are still propogating out to other projects in the company. Some of the changes required business-wide process changes and a lot of time (I have no idea how much) to other people in the company. I am guessing it cost the company around a million to 2/3 of a million dollars.

In this particular instance, it will probably take another 40+ work-years before the change time and development cost is recouped by the company. But the developers are happier and percieved productivity is improved, so the non-financial benefit is worth it.

So...

What exactly is your goal? What are you trying to improve? TCO? API independance? Developer sanity? Long term design? Figure out the one (and only one) thing you are trying to achieve, and the rest will follow from it.
We use various factors to grade the feasibility for a rewrite, pretty much along the lines of the following:

(numer of developers who contributed to the original source base)
(average skill of developers who contributed most of the current source base [1-10])
(special skills required [0-10]?)
(overall complexity of project [1-10])
(complexity of current source/module [1-10])
(reusability of current source/module [1-10])
(lines of current source code in KLOC)
(development time years or hrs)
(number of distinct modules/units/files)
(amount of platforms that need to be supported)
(age of module/project, when orignally written in years)
(number of years when the module/project was last updated)
(source code intuitivity [0-10])
(overall maintainability score [0-10])
(how well it interacts with the rest [0-10])
(percentage of requirements that are met [0-100%])
(number of project dependencies)
etc.

and then we compare this with the effort required for a rewrite or major redesign (or even refactoring). By keeping everything as a distinct category, you can easily prioritize certain components-depending on your requirements. In the end, you'll see if it s worth it or not.

If you are into maths, you could probably plug all these factors into some sort of nifty formula to make sense of it ;-)


Welcome to Code Smells.
My experience is that 95% of the time, careful refactoring is the best way to go.


1) Make a design on paper on how you would _like_ the design to be.

2) Select the most troublesome area of your current design and do a number of careful refactoring steps to turn that part into the new design. After each step, everything must still work properly. If it stopped working, fix it _now_.

Your program is now much better and still works.

If needed, repeat from 2) above.


When refactoring you will often add temporary compatibility code. This is the price you pay for the safety of making many small and safe steps of improving the design. It's almost always worth it, though.

Joel on Software


My 2 cents.


/Marcus

This topic is closed to new replies.

Advertisement