tags:

views:

970

answers:

22

What do you do when you're assigned to work on code that's atrocious and antiquated to the point where it's almost incomprehensible?

For example: hardware interface code, mixed with logic, AND user interface code, ALL in the same functions?

We see bad code all the time, but what do you actually do about it?

  • Do you try to refactor it?
  • Try to make it OO if it's not?
  • Or do you try to make some sense of it, make the necessary changes and move on?
+3  A: 

You try to refactor it, in the strict sense on the word, where you're not changing the behaviour.

The first target is usually to break up giant methods.

Leon Bambrick
what if you break something ? who is paying for re-testing it all ?
Preets
It won't matter since you would of course write comprehensive tests before you touched the code.
EBGreen
+27  A: 

Depends on a few factors for me:

  1. Will I be maintaining this code in the future, or is it a one-off fix?
  2. How long until this system is replaced entirely?
  3. How busy am I at the moment?

Ideally, I'd refactor all bad code I had to maintain, but the reality is there are only so many hours in the day.

Pat
+1 Totally what I was just about to answer. All the "refactor it" answers do not take into account that when you are working on this kind of code you usually never have the time to actually clean it up. The irony is that the code usually only get's that ugly because of some people hacking into there as if there was no tomorrow. This always reminds me of the "borken window theory" mentioned in "The Pragmatic Programmer".
galaktor
"How long until this system is replaced entirely?" - never. Just so you know.
Michael Kohne
A: 

Depends on your time frame and how important that code is to you. If you have to "just make it work" then do that and rewrite the module when time allows.

If its an important or integral part of what you do then refactor refactor refactor.

Then find the guy/girl who wrote it and send them a rude postcard!

David Archer
+7  A: 

The book Working Effectively with Legacy Code discusses the options you can do. In general the rule is not to change code until you have to (to fix a bug or add a feature). The book describes how to make changes when you can't add testing and how to add testing to complex code (which allows more substantial changes).

Kathy Van Stone
+2  A: 

Post it to www.worsethanfailure.com!!!

Zoidberg
+10  A: 

As is frequently the case, "It Depends".

I tend to ask myself some of the following questions:

  • Are there unit tests for the existing code?
  • Is refactoring the code an acceptable risk for my project?
  • Is the author still available to clarify any questions I might have about the code?
  • Will my employer consider the time spent on changing existing, functioning code to be an acceptable use of my time?

And so on...

But assuming that I have the capacity to do so, refactoring is preferential as the up front cost of fixing the code now will likely save me a lot of time and effort later in maintenance and development time.

There are other benefits as well, including the fact that the more clean and well maintained you keep your code base, the more likely other developers are to keep it that way. The Pragmatic Programmer calls this the Broken Window Theory.

Dan Rigby
+1 for the Broken Window Theory. Very interesting read. Thanks!
JasCav
+1  A: 

I'd talk to my manager and describe the code. Most managers would not want a program held together by banding wire and duct tape per se. If the code is really that bad there are sure to be some business logic errors, hardcoding etc. stuffed in there that will eventually just destroy productivity.

I've come across some pretty bad code before (single letter variable names, no comments, everything crammed onto one line, etc.) and once I mentioned/showed it to my manager they almost always said "go ahead and re-write it", because not only are you taking the hit for reading and changing the code but future co-workers will have to go through the same pain. Better that you take a longer period of time just once to rewrite it rather than having each person who touches the code in the future have to go through and comprehend and decipher it first.

Scott Vercuski
+2  A: 

Given the strength of some of the adjectives you use, i.e. atrocious, antiquated and incomprehensible, I'd bin it!

If it is in such a state, like the example you give, it's probably not got any test code for it either. Refactoring is mentioned in many of the other answers but, sometimes, it is not appropriate. I always find that, when refactoring, you generally need a clear path through which the old code can be gradually morphed into the new in a number of well defined steps.

When the old code is so far removed from how you want it to look, such as the extreme cases you seem to be suggesting, you could probably redesign, rewrite and test the new code in a shorter time than it would to take to refactor it.

Steg
+2  A: 

Scrap it and start over, using the compiled legacy application as a business requirements document. And spending time in analysis with the users to see what they want changed.

Beth
+2  A: 

If no modifications are needed, I don't touch it.

If at all possible, I write automated unit tests first, especially focused on the areas that need modification.

If automated unit tests are not possible, I do what I can to document manual unit tests.

I am just using the tests to document "current" behavior at this point.

If possible, I always keep a version of the code and executable environment that runs things the "original" way (before I touched it) so I can always add new "behavior documentation" tests and better detect regressions I may have caused later.

Once I start changing things, I want to be very careful not to introduce regressions. I do this by continually rerunning (and or adding new tests) to the tests I wrote before I started writing code.

When possible, I leave bugs as-is if there is no business need for them to be fixed. Those bugs may be "features" to some users and may have unclear side effects that wouldn't be clear until the code was redeployed to production.

As far as refactoring, I do that as aggressively as possible, but only in the code that I need to change otherwise anyway. I may refactor more aggressively in my own personal copy of the code that will never be checked in, just to improve the readability of the code for me personally. It's often times difficult to properly test changes that are only made for readability reasons, so for safety reasons, I generally don't check those changes in / deploy them unless I can confidently test that the code changes are completely safe (it's really bad to introduce bugs when you are making changes that are unnecessary for anything but readability).

Really, it's a risk management problem. Proceed with caution. The users do not care if the code is atrocious, they just care that it gets better without getting worse. Your need for beautiful code is not important in this scenario, get past it.

Michael Maddox
A: 

The worst offender (in my experience) of really AWFUL code is the ease with which people can do cut & paste these days. Cut & paste should be used rarely. If you think that's the right solution, it's generally better to step back and generalize the problem a little.

xcramps
+8  A: 

Developers have an instinct to assume that code is always ugly because of other, inferior developers. Sometimes, code is ugly because the problem space is ugly. All that ugliness isn't just ugliness - it is sometimes institutional memory. Each line of ugly in your code probably represents a bug fix. So think very carefully before you rip it all out.

Basically, I would say that you shouldn't touch code like this unless you actually have to. If there's a real bug that you can solve, refactoring is reasonable, if you can be sure you're maintaining the same amount of functionality. But refactoring for the sake of refactoring (eg, "make the code OO") is what I would generally classify as a classic newbie mistake.

peterb
This is a good reminder that we all too often love to forget about.
Kyle Walsh
Would you refactor code (say a function specifically) so that it is easier to implement your fix and possible future fixes? (I agree, refactoring just to 'make the code oo' IS a classic newbie mistake. I guess what I meant was more in line with 'make it more OO so that the code adheres more to Solid principles in an attempt to make code changes easier at the moment and in the future.'
MunkiPhD
But at some point you SHOULD just rip it all out and start anew; take a look at http://blogs.msdn.com/ericlippert/archive/2009/06/01/bug-psychology.aspx
RCIX
+1  A: 

Just like any other code, you leave it slightly better when you leave it than it was when you entered it. You do not ever, ever rewrite the whole code. If that is the work it takes for some reason, then you start a project (small or large) for it.

I am assuming we are talking about a substantial amount of code here.

Not every day is a great day at work you know :)

Johan Rylander
A: 

Im my company we always Refactor Mercilessly. so we still come across atrocious code but LESS and Less and less ...

We write a lot of in-house code and the company is run for about 100 years by the same family. Management usually tells us we have to maintain the code base (evolve) for another 50 years or so. In this setting having code you don't dare to touch is considered a bigger risk to the long term survival of the company then the prospect of downtime because some under-tested code broke because of refactoring.

mdorseif
A: 

Anytime you see code that is "nearly incomprehensible", PROCEED WITH CAUTION. You need to assume that any major re-factoring will result in new bugs being introduced that you'll need to find and correct.

Additionally, I've seen this scenario many times (even fell victim to it myself once or twice): Programmer inherits legacy code, decides code is ancient & unmaintainable and decides to refactor it, ends up deleting key "fixes" or "business rules" subtly patched in over the years, ends up spending a lot of time tracking down and re-introducing similar code when users complain about "a problem fixed years ago is happening again".

Re-factoring (and debugging) almost always takes longer than expected and should never be considered as a "freebie" that comes along with whatever task you're supposed to be doing.

"If it ain't broke, don't 'fix' it" still has a lot of truth.

Mark
A: 

I run copy-paste detector and findbugs on all legacy code that comes my way.

I then plan my initial refactoring:

  1. remove unused code, unused variable and unused methods
  2. refactor duplicated code
  3. set up a single step build
  4. build a basic functional test

By that point the code meets the basic minimum for maintainability. It can be easily built and basic errors can be found via an automated test.

I often add code like this:

log.debug("is foo null? " + (foo == null));

log.debug("is discount < raw price ? " + (foo.getDiscount() < foo.getRawPrice()));

Some of that code will be recovered for unit tests when I can refactor to it.

sal
+2  A: 

There is an old saying. If is isn't broke, do not fix it. If you have to maintain it then reverse engineer it and document it so the next time you come across it you will know what it does.

You do not know the situation the developer was in when he or she wrote the code. He or she may have been under a time crunch when it was written, (management was all over the developer, etc)

There are also situations where he or she wrote the code per the spec, The spec then changed several times, the developer had to patch the code, as rewrite is out of the question due to time constraints. This happens all of the time.

If the code impacts the performance of robustness of the application and is modular then you can re factor or re-write. Document the situation to assist future programmers in understanding.

Also many programmers consider reverse engineering other developers code as beneath them. they would rather rewrite without considering the ramifications of doing so.

If you have never done so, try it sometime, it will make you a better developer.

Thanks

Joe

Joe Pitz
+1 If you refactor and then make bug fixes, it can be difficult to analyze the diff of the code that contains the bug with the code as it exists after the bug fix.
Les
A: 

I've worked places where we ship that kind of code.

A: 

I try to make sense of it, make the necessary changes, and move on.

Of course, making sense of it usually involves some changes; at the very least, I move around the whitespace and line up corresponding braces in the same column like so:

if(condition){
doSomething(); }

// becomes...

if(condition)
{
    doSomething();
}

I'll also often change variable names.

And very often, "the necessary changes" require refactoring. :)

Imagist
+1  A: 

Kill it with fire.

+2  A: 

The first question to ask is: does it work?

If the answer is yes, that would be a huge disincentive to simply ditch it and start over. There may be thousands of man-hours in that code which address edge cases and nasty bugs. Worse yet, there may be other modules in the system that depend on the current incorrect (but known and possibly documented) behavior. Don't mess with it if it isn't broken.

If you are keen on cleaning it up, start by writing test cases for the current behavior. When you run across an instance where the behavior differs from the specification, you must decide whether to accept the behavior as "correct" or go with what the spec say it ought to do.

Only once you have written test cases that all pass should you begin to refactor. The tests will tell you whether your efforts are breaking anything.

Barry Brown
A: 

Get the idea of what they're doing and the deadline to finish. A larger deadline, typically rebuild much of the code from the ground up, as I find it a very worthwhile experience to not only decipher terrible code and make it legible and document, but somewhere in your brain those neurons are pressed to avoid similar mistakes in the future.

mduvall