views:

317

answers:

10

scenario: you have tightly coupled legacy code that you can not unit test. and you have to bug fix, what do you do to not get very depressed? I read working effectively... and refactoring.. by fowler, however in both book unit testing is indispensable. Refactoring is out of the question and a code rewrite is on the table however it will take a long time. suggestions?

A: 

Include tons of debug output of strategically interesting variables or points in the code (e.g. inside an if branch or in the else branch). Then try and get the code to be executed automatically. You might want to use recorded mouse movements, bash scripts or the like. Then see what happens in the code and compare with what should happen.

You might also want to read "Why Programs Fail" by Andreas Zeller.

xmjx
A: 

BTW this is Delphi code in a non desktop application

Miau
+1  A: 

The problem with that kind old legacy code is the number of side-effects that can manifest themselves when you make the slightest modification.

A possible solution is to have a good "pre-production" (or "production-like") environment in which you can make some "parallel testing" (that is compare the comparison of your outputs with the outputs of the production).

Note: that kind of test should not always return 100% match, because it would mean you match also the very bug of the system in production that you are trying to fix! Hence the difficulty of such parallel tests...

That way you can make incremental modifications and have a reasonable assurance as to the end-result of your modified legacy code when it will be released...

VonC
+5  A: 

Debug the code the old-fashioned way, by finding a way to consistently reproduce the bug, breaking in with a debugger at the earliest point where state is detectably wrong, looking where that state came from and repeating until you find the source of the problem. Sprinkle assertions through the code as you go, working out what the relevant invariants are will help you understand what's going on and learn your way around the codebase.

Once the firefighting is over, decouple and refactor as you go.

moonshadow
+3  A: 

I'm working with some code these days that sounds like it's in a similar situation. We operate under the assumption that the existing code is correct, however confusing or just plain bad it might look. There must be something you can do to perform some kind of regression test. We have some carefully prepared inputs that we run through both the original system and our modified system to ensure that there are no unexplained changes in behaviour. I also recommend the book Working Effectively with Legacy Code.

Greg Hewgill
A: 

You have my sympathies... I was in a similar situation at my last job, working on a massive C++ COM and VB6 app.

The only suggestion I can offer is to do manual testing and ensure that the app behaves the same before you changed it as after.

Unit testing code usually required code that was written with unit tests in mind, which doesn't work well for legacy code.

Have a very understanding QA department with an attention to detail and a high boredom threshhold is what we did, I'm not sure that is available to everyone though...

Good luck.

Omar Kooheji
+1  A: 

You adapt to the problem and get on with it.

If more bugs arise out of fixing the bugs you need to explain to your employer exactly why this occured and why it would be better to NEVER let that mess happen again. This is how change occurs in a corporate environment. What you shouldn't do is go in there and do an absolutely perfect job without raising the problems you're having (your low morale counts as a problem) working with that code. If you want stuff like Unit Testing to become a de facto standard, you need to explain the benefits - technical and business.

People had to refactor and bug fix without unit testing for decades before all this Unit Testing stuff came along, just find the bugs and fix them as best as you can. Then cross your fingers.

David McLaughlin
A: 

The problem is not

  • reproducing the bugs
  • realising that we need to rewrite

the problem is how do you write better code in this tyoe of programing environment how can you improve the quality of the code where you are "fixing" something that you have little time to fix and you have several global variables of some sort that relate to the problem you are trying to solve

and Cross fingers is not a solution is just what you end up doing

Miau
+1  A: 

I've been thinking about this a lot over the last week - it's a complicated subject because of the amount of possible variety that each different legacy code mess presents. There isn't a general answer - it always comes down to how much time you spend on it, and the quality of your experience and ability.

To specifically answer this question in general and the last point in particular - it is best to use the code to get inside the mind of the original developers, and to keep reminding yourself that the code isn't that bad - really helps you to stop getting disheartened. Assume everything was done for a good reason, with good intent - hindsight may have shown otherwise.

If the code is complex and works - then you may even find that rewriting it isn't going to be that easy; experience reminds me that often a rewrite can fail to work as well as the original - but have a much nicer architecture...

Global variables aren't always bad - they have their usage - but can be a sign of a programmer who doesn't think in modules. A read only global variable is often necessary, however with read-write you need to consider ownership and a protocol for modifying it.

As for not being able to use any form of unit testing framework - you're probably right, but that doesn't mean that you can't have automated testing. What you probably need is something more like a test harness. This sits around the code, sets up the environment and calls the routines, comparing the output. It does the same job as unit or module testing but works with any system.

To write better code into the system you need to find a way to ease new stuff in whilst not breaking what is there. I can't tell you how to do this, it's something that you have to use your own creativity and judgement to find - but there will be a way of gradually migrating - software is infinitely malleable.

Richard Harrison
A: 

An alternative to unit-tests when addressing legacy code is introduced in the book Working Effectively with Legacy Code :

Characterization tests are tests that pin down (characterize) the current behavior of the code and sense when changes invalidate it. Quoting Wikipedia:

The goal of characterization tests is to help developers verify that the modifications made to a reference version of a software system did not modify its behaviour in unwanted or undesirable ways. They enable, and provide a safety net for, extending and refactoring code that does not have adequate unit tests.

philippe