views:

1033

answers:

14

I think back to Joel Spolsky's article about never rewriting code from scratch. To sum up his argument: The code doesn't get rusty, and while it may not look pretty after many maintenance releases, if it works, it works. The end user doens't care how pretty the code is.

You can read the article here: Things You Should Never Do

I've recently taken over a project and after looking through their code, it's pretty awful. I immediately thought of prototypes I had built before, and explicitly stated that it should not be used for any production environment. But of course, people don't listen.

The code is built as a website, has no separation of concerns, no unit testing, and code duplication everywhere. No Data layer, no real business logic, unless you count a bunch of classes in App_Code.

I've made the recommendation to the stake holders that, while we should keep the existing code, and do bug fix releases, and some minor feature releases, we should start rewriting it immediately with Test Driven Development in mind and with clear separation of concerns. I'm thinking of going the ASP.NET MVC route.

My only concern is of course, the length of time it might take to rewrite from scratch. It's not entirely complicated, pretty run of the mill web application with membership, etc..

Have any of you come across a similar problem? Any particular steps you took?

Thanks a bunch!

UPDATE:

So.. What did I end up deciding to do? I took Matt's approach and decided to refactor many areas.

  • Since App_Code was getting rather large and thus slowing down the build time, I removed many of the classes and converted them into a Class Library.
  • I created a very simple Data Access Layer, which contained all of the ADO calls, and created a SqlHelper object to execute these calls.

  • I implemented a cleaner logging
    solution, which is much more concise.

While I no longer work on this project [funding, politics, blah blah], I think it gave me some enormous insight into how bad some projects can be written, and steps one developer can take to make things a lot cleaner, readable and just flat out better with small, incremental steps over time.

Thanks again to everyone who commented.

+10  A: 

Joel's article really says it all.

Basically never.

As Joel points out: you'll simply lose too much doing it from scratch. It'll probably take way longer than you think and what's the end result? Something that basically does the same thing. So what's the business case for doing it?

That's an important point: it costs money to write something from scratch. How will you recoup that money? Many programmers ignore this point simply because they don't like the code--sometimes with justification, sometimes not.

cletus
Even if it's a god awful mess, where there is basically no exception handling, no logging, performance issues at peak times?
Jack Marchetti
Of those only "performance issues" really matters. And what's the issue? A month of dev time can easily buy an 8 core server with 32GB of RAM. If that solves your performance problem, do that instead. The rewrite and inevitable bugfixes will take way longer.
cletus
What if your customers refuse to buy any newer hardware? Our company is limited to machines that run OpenGL 1.4, if we're lucky, and we have to deploy to whatever was the lowest end Dell that was sold three years ago. We can't just throw more hardware at the problem, because then our customers would refuse to buy from us.
mmr
It's running on some pretty hard core equipment now from what I understand and still has some bottle necks.What about an incremental re-write, where ya know, you actually have a data layer, so one class doesn't have SqlConnection conn = new Sqlconnection();twenty five times.
Jack Marchetti
I'd agree with the point that there needs to be a business case for doing it. From the customer perspective, what changes? Can requested features be implemented more quickly? Would the code scale better or handle load more efficiently (Back it up with some actual numbers here, if possible)? Joel is right - though I hate messy code too, you'd be asking new requests to wait behind a project that has no impact at all on them, and that's just going to be frustrating.
rwmnau
@mmw: a rewrite that will allow you to support older/slower hardware or will allow you to do something of commercial value that you otherwise can't (or can't do at a reasonable cost) now is the start of a business case.
cletus
@JackM: the "low hanging fruit" principle probably applies in that there will probably be some simple things you can do that'll make a large difference. While I can sympathize with ugly/bad code (believe me I can), the low hanging fruit approach is probably the lowest cost/risk approach.
cletus
@cletus: see my post below. If only I worked with such sane, rational people that would understand what a 'business case' is. I need a new job.
mmr
@cletus -- I think I'm definitely going to take the "not from scratch" approach. I think my first goal will be to introduce a data layer, which will cut down on a A LOT of code duplication.
Jack Marchetti
How will you recoup that money?--What if more money was being spent to maintain the buggy app. What if the downtime of the app caused loss of money. What if really top management like CIO starts disliking the poor image the app is causing among the business users.
Pratik
@pratikk: those are all factors that can be used to create a business case.
cletus
+7  A: 

I have had such an application, and rewrite was very rewarding. However, you should try to aviod the "improvement" trap.

When you rewrite everything, it is very tempting to add new features and fix some long-standing issues you didn't have the guts to touch. This can lead to feature creep and also extend the time needed for rewrite enormously.

Make sure you decide what exactly will be changed and what will only be rewritten - in advance.

Milan Babuškov
Corollary: make sure that you have known standards from which to compare. If you start to rewrite to optimize, you have to make sure the previous code works, and that the code you write will reproduce the exact functionality of the old code. Otherwise, you're making new code rather than improving old code.
mmr
+1  A: 

At some point, you have to cut your losses. If you've just inherited this code base, you might make changes that have unintended consequences, and due to the lack of tests, they'll be nearly impossible to find.

At the very least, start writing tests immediately.

Matt Grande
I personally find it really hard to write tests against code that isn't written with testability in mind.If his code is as bad as he says it is, writing tests immediately might very well not be an option
borisCallens
+4  A: 

Only one quasi-legitimate reason comes to mind: politics.

I've had to rewrite a codebase from scratch, and it had to do with politics. Basically, the previous coder who managed the codebase was too embarrassed to release the source code to the new team that had just been hired. She felt that every criticism of the code was a criticism of her as a person, and as a result, she only released code to the rest of us when she was forced. She is the only person with administrative access to the source repository, and whenever she's been asked to release all the source, she's threatened to quit and take all of her knowledge of the code and go home.

This codebase is over 15 years old, and has convolutions and contortions from various different people with various different styles. None of those styles apparently involved comments or specifications, at least, in the small portions she's released to us.

With only partial code available and a deadline, I was forced to do a total rewrite. I got yelled at as a result, because it was claimed that I caused a serious delay, but I just kept my head down and got it done rather than argue.

Politics can be a huge pain.

mmr
Wow what a clusterf###. Talk about insecure haha Yeah I mean, the site "works" right now, but there's a reason why the previous firm lost the contract, and now the client I'm at has it. And seriously, it's some really bad code.
Jack Marchetti
LOL this reminds me of a place where I got in trouble for not writing an HTML editor as a Swing component in 2 weeks with advanced drag and drop functionality because it was politically untenable to go out of the organisation and buy something already done. That thing became the bane of other programmers for another 3 years before it got rewritten as a Web app and thus became a non-issue.
cletus
Hilariously, the president has just forced another Java developer here to rewrite the UI of another product using custom, non-swing controls because he doesn't like the look of Java native controls. Which begs the question: Why is the president of the company concerning himself with how buttons look?
mmr
That programmer sounds absolutely ridiculously childish. How on earth is she still actually employed by the company?
Kirschstein
I'd post that, but I'd be concerned about airing _too_ much dirty laundry. If future employers go through my SO posts, all that they need to know is that I've done a full rewrite, but I wasn't happy about it, and they shouldn't put people in these kinds of situations.
mmr
"Why is the president of the company concerning himself with how buttons look?" I wonder if Steve Jobs worries about how buttons look. I think maybe "yes."
Nosredna
This guy is no Steve Jobs. I'll leave it at that.
mmr
+28  A: 

Just because it has all those problems now doesn't mean it has to continue to have them. If you find yourself making a specific bug fix in the system that could benefit from, say, a new data layer, then create a new data layer. Just because the whole site doesn't use it doesn't mean you can't start using one. Refactor as you need to during your bug fixes. And make sure you understand exactly what the code is doing before you change it.

Problem with code duplication? Pull it out into a class or utility library, in a central location next time you have to fix a bug in the duplicated code.

And, as already mentioned by other responders - start writing tests now. It may be hard if the code is a coupled as it sounds, but you can probably start somewhere.

There is no good reason to rewrite working code. However, if you are already fixing a bug, there is no reason you can't rework that specific part of the code with a "better" design.

Matt
that's a pretty great idea. kind of rewrite it without doing it from scratch.
Jack Marchetti
+1. That's the only sensible way to do it. You don't need much approval of bosses (I'm actually fixing the bug/implementing a feature) and you improve the state of things
Vinko Vrsalovic
+4  A: 

I have been in precisely this situation but rather than a total rewrite I worked to change things through a refactoring process. The problem I ran into was the enormous complexity of the code I was working with- many pages of horrible, special-case-driven development all based on if-cases and convoluted regexes layered back over about ten years of unplanned growth and expansion.

My aim was to get it refactored function by function so that it would provide the same output for the same inputs but work much more cleanly and smoothly under the bonnet to facilitate future growth and improve performance. The general solution was clean and quick but the fixing job on the code just got more and more difficult and complicated as obscure special-cases in the documents being parsed by the system started to show themselves and my nice clean code would generate output that was just a little too different from what the original did ( this was web pages, so a different amount of whitespace could cause all kinds of layout problems on older IE versions ) in small and obscure ways.

I don't know if the reworked code ever got used- I left the company before it had the chance to be fully integrated- but I doubt it. Why use twenty lines of code when fifteen hundred 'if' statements and three-line regular expressions could do the same job?

glenatron
+3  A: 

Instead of a complete rewrite from scratch you want to start refactoring the code base in small steps while introducing unit tests. For example

  1. Move duplicate code into a common class with tests for resuse throughout the project
  2. Introduce interfaces to create separate testable modules. You can then refactor the implementation behind the interface while relying on your tests to ensure you don't break anything.
Mark
+3  A: 

I would rather do things bit by bit, e.g., create a back-end to the database with a data model as you work in those areas (i.e., user login first, then user management, and so on), and tweak the existing front-end to use the new back-end (interface driven, so you can also add tests). This will keep the existing code with possible undocumented tweaks and behaviours that you wouldn't replicate by developing again from scratch, whilst adding in some separation of concerns.

After a while you will have migrated some 60% of the code base to use the new back-ends without the work being an official project, just maintenance, so you will be in a better position to argue for development time to do the other 40%, and once that is done the existing front-end classes will be vastly reduced in size and complexity. Once it is fully migrated, you will be able to reuse the new back-end model and controller components if you ever get the time to implement a new view.

JeeBee
+3  A: 

I disagree with that article somewhat. For the most part Joel is correct but there are counter-examples that indicate sometimes (even if rarely) a rewrite is a good idea. E.g.,

  • Windows NT (Broke away from the old DOS code-base. Upon this foundation was built Win2k, WinXP and the upcoming Win7. Yes, Vista too. The last version of Windows on the old base was the infamous WinME)
  • Mac OS X (Rebuilt their flagship product on FreeBSD)
  • Many cases where a competitor displaces a de facto standard. (e.g., Excel vs. Lotus 123)

I believe Joel's argument is mainly based on fairly well-written code in the existing version that could be improved with hindsight. By all means, if the code you inherited is really that bad, push for a rewrite--there's some scary stuff out there. If it's at all tolerable and works reasonably well, phase in the new stuff at a slower pace.

steamer25
Nah I think Joel was trying to state that while you think it's bad, it's mainly due to the core principle that code is easier to write, than it is to read. Granted, the code I've inherited has some severe architecture problems, but from what I've gathered, an incremental approach would be more apt.
Jack Marchetti
+1  A: 

Start by writing a technical spec. If the code is that awful, then I bet there isn't a real spec either. So write a comprehensive and detailed spec - you need to write a spec anyway if you want to rewrite from scratch, so the time is a good investment. Be careful to include all details about the functionality. Since you are able to investigate the actual behavior of the app, this should be easy. Feel free to include improvement suggestions, but be sure to capture all details of the current behavior.

As part of the investigation you might consider writing some automated tests of to system to investigate and document expected behavior. Focus on black-box/integration testing rather than unit-testing (which the code will probably not allow anyway if it is that ugly).

When you have this spec you will likely discover that the app is actually much more complex than your first impression, and reconsider rewriting from scratch. If you decide to gradually refactor instead, the spec and tests will help you a lot. But if you still decide to go forward and rewrite, then you have a good spec to work from now, and a suite of integration tests which will telly you when your work is complete.

JacquesB
+2  A: 

One danger in a complete rewrite is that your job is constantly on the line. You're a cost that isn't contributing to the bottom line. The code that sucks is the code that's making the money.

But if you fix the existing code one piece at a time, you're the guy who knows how the money machine works.

Nosredna
+1 for you. So true - the code that sucks is making the money. In many respects you have one shot on a project, as once the business peeps get it in their heads that they have an "investment" all other alterations for the better go against the grain.
David Robbins
+6  A: 

The book Facts and Fallacies Of Software Engineering states this fact: "Modification of reused code is particularly error-prone. If more than 20 to 25 percent of a component is to be revised, it is more efficient and effective to rewrite it from scratch." The numbers come from some statistical studies performed on the subject. I think the numbers may vary due to the quality of the code base, so in your case, it seems to be more efficient and effective to rewrite it from scratch by taking this statement into account.

swamplord
And I think that's actually an important point. IF you have to significantly grow upon an existing, messy, codebase, it makes sense to start over. As opposed to rewriting something "just because it looks bad"
Andrei Taranchenko
+1  A: 

My answer is: rewrite from scratch as often as possible.

I've spent most of my career inheriting steaming piles of dung we politely called "programs", written by young, inexperienced programmers who were considered "rock stars" by the managers. These things are generally unfixable, and you end up spending 10 times as much effort keeping them limping along as you would have spent just rewriting them from the ground up.

But I've also benefited tremendously by rewriting my own work periodically. Every rewrite is a chance to do things differently and potentially better, and you should be able to reuse at least some parts of the older version.

That being said, not all rewrites are a good idea. Windows Vista, for example.

MusiGenesis
Well if a place is relying on a young, inexperienced guy they get what they deserve.I typically have run into sites built by designers, who think they're developers. I wonder when people will realize that even though it's a website, it does take some engineering skill to get these things to work properly.
Jack Marchetti
Relying on young programmers is very tempting, because you can pay them very little while praising them out the wazoo. This practice has very unfortunate consequences, however.
MusiGenesis
+1  A: 

I have been part of a small dedicated team that has rewritten code from scratch including reverse engineering business rules of the earlier code. The original application was web service written in C++ (with regular crashes and severe memory leaks) and a ASP.Net 1.0 web application and the replacement was a C# 2.0 asmx based web service and an ASP.Net 2.0 web application with Ajax. That said some of the things the team did and explained to management

  1. We supported the existing code base in production until the new code was ready.
  2. The management agreed that the rewrite (first release) would introduce no new features but just implement existing features. We added only 1-2 new features at the end.
  3. The small team was comprised of very experienced developers with excellent understand ability and cooperation.
  4. It was harder to get C++ talent in the organisation and C# was seen as a better alternative for future maintenance.
  5. We agreed to an aggressive timeframe but at the same time were confident and highly motivated to work in C# 2.0, ASP.Net 2.0 etc.
  6. We had a team leader to shield us from upper management and we followed scrum like process.

The project was highly successful. It was very stable and much better performing. Later it was easier to add new features. So I believe that code rewrite can be successfully done given right resource and circumstances.

Pratik