The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Coding for the debugger

Despite its many detractors, I find the debugger a marvellous tool. Get past my preferred level of "run, see failed assert, examine variables, fix", though, and I find one can rapidly become bogged down. My preferred strategy is to use asserts and logging, with the debugger being for post mortem examination, but there are many times when one has to use the debugger to single step the code and examine its state by hand. In order to keep this fairly efficient, I write code bearing in mind the limitations of the debugger.

When I was at university I recall one lecturer suggesting that constructs such as:

  if(this->GetFlag())
    return true;
  else
    return false;

should be replaced with:

  return this->GetFlag();

And in a sense, he is right -- but this makes it near-impossible to breakpoint a particular case. And I see this kind of thing a fair bit:

T *GetItem() {if(!m_item) this->CreateItem(); return m_item;}

Presumably written in the interests of saving screen lines (or making the inline function run a bit faster?!), this suffers from a similar problem, in that you can't breakpoint any of it, and that single-stepping often has to take place at the disassembly level.

Other tactics I use include minimizing use of complex macros, copying STL iterators into pointers as soon as possible, performing common subexpression elimination and invariant hoisting by hand, using enums rather than #defines, and generally making it so that the program text contains things that can be drag'n'drop'ed verbatim as a watch expression.

This is probably a symptom of having a crap debugger (Visual Studio .NET), but most debuggers have similar limitations, particularly in the variable watching department, and in any event you don't always get to choose which to use. I'm more interested in whether there are others out there who code with such things in mind, because I haven't met many, and whether you've found -- I have, to an extent -- that the readability of the code has improved at all as a result.
Tom_
Monday, March 06, 2006
 
 
Yes, using and "coding for" debuggers is pretty standard in the software development industry, at least everywhere I've been.
Bill
Monday, March 06, 2006
 
 
"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?"
- Brian Kernighan

Something else to think about: which usually is the high pressure situation, coding or debugging? 

When it really counts, make it as straightforward as possible.  Write to make it easy for a tired stressed person to figure out what's going on and fix it.

Of course if you do that, a side effect is that you won't have as many things to fix...
rkj Send private email
Monday, March 06, 2006
 
 
Sorry, debugging is usually used to replace thinking. I am not saying it's never necessary, but I can't imagine debugging so often I would have to code for it.
son of parnas
Monday, March 06, 2006
 
 
I do this sort of thing all the time.  It happens a lot in C# for things like catches:

try {
//code
} catch (Exception ex)
{
ex.ToString();
//code
}

I really dislike have the extra "ex" and the superfluous "ex.ToString()", but it does make debugging easier.  Usually what I do is leave these things in when I am doing the prototype of some code, but then remove them from the first "refactored" version.  I end up doing the following also:

string temp = boom("somestring");
temp = baaz(temp);
string foo = bar(temp);

instead of :

string foo = bar(baaz(boom("somestring")));

That way I can check the state after each function call, and I can do it by just hovering my mouse over any instance of temp as temp changes (VS.NET 2003).  Coming from a Lisp/Scheme sort of background in college, it is almost appaulling to me to find this sort of code in the C# I write, but one has to bend to the environment in which one programs. 

Also, I think this is caused by not having an interactive command line interface like several of the higher level languages offer (Lisp/Scheme/Python etc).  If you can test each function individually before putting them all together in a single line, then you have more confidence in the final result.
Joshua Volz Send private email
Tuesday, March 07, 2006
 
 
Sure, I do that all the time (though people on my team do think I'm a little weird).
But you are not going far enough :)

if(this->GetFlag())
  return true;
else
  return false;

should be

result = this->GetFlag();
if( result )
  return true;
else
  return false;

that way when you're debugging you can change the value of 'result' after GetFlag and before the if. This can be a huge time saver in certain complicated situations.

I can't stand code where you compact seven function calls, three tests and four auto-incs into a single line.
Parisian developer available for cute geek girls
Tuesday, March 07, 2006
 
 
>>>>>>>>>
I end up doing the following also:

string temp = boom("somestring");
temp = baaz(temp);
string foo = bar(temp);

instead of :

string foo = bar(baaz(boom("somestring")));
<<<<<<<<<

So do I. The former is a better reflection of what I'm thinking when I'm coding; the names of the temporary variables help to clarify my intent; and, I reckon that an optimizing compiler should be able to optimize them away.
Christopher Wells Send private email
Tuesday, March 07, 2006
 
 
> string foo = bar(baaz(boom("somestring")));

This is just unclear. I work with people all the time who do this crap and they don't use blank line or whitespace or anything else that takes typing.

>result = this->GetFlag();
> return result;

I do this so I can log the result value.

> If you can test each function individually
> before putting them all together in a single line,
> then you have more confidence in the final result.

At time T1. But after changes at time T2 you have to unpack the line again to figure what screwed up. I try not to ever play the I know it works card so I can make more complicated constructs. It won't work later at some point.
son of parnas
Tuesday, March 07, 2006
 
 
<em>When I was at university I recall one lecturer suggesting that constructs such as:

  if(this->GetFlag())
    return true;
  else
    return false;

should be replaced with:

  return this->GetFlag();</em>

When I was professionally tutoring, I used to really push students to write code like this because I wanted to make sure they broke through the mental barrier to actually understand that the statements are equivalent.  However, I agree that it makes debugging difficult.

Additionally, as an academic tutor, I had to deal with other constraints.  Generally, it is considered poor programming technique, academically, to have more than one return path from a function.  Thus, I would push alterantively for code similar to this:

<em>
bool result = this->GetFlag()
return result;
<em>

I feel that this solves the verbosity issues as well as the debugging issue while keeping the code academically correct.  I generally carry on this practice even today.
newby
Tuesday, March 07, 2006
 
 
At time T1. But after changes at time T2 you have to unpack the line again to figure what screwed up. I try not to ever play the I know it works card so I can make more complicated constructs. It won't work later at some point.
--son of parnas

Actually, I was talking about doing it with an interactive interpreter, like those that exist for Scheme or Python (and I am sure a bunch of other languages).  So, even at time T2, I can still open the interpreter, test each function individually if need be, without ever having to alter the actual code in my program. 

Don't get me wrong, in a language like C/C++/C#/Java I would avoid composing multiple functions in a single line unless there is some compelling reason to do so. 

This discussion makes a strong point about how the tools that we write code in affect how we write the code.
Joshua Volz Send private email
Wednesday, March 08, 2006
 
 
If you were in 'real' engineering you would have no trouble designing for testability, putting test points on circuit boards etc..
But in programming we act as if not expecting it to work first time is some sort of hack.

ps- to academic programmer, ask your students if they expect the compiler to produce substantially different code in the two 'if' examples..
Martin Send private email
Wednesday, March 08, 2006
 
 
>Generally, it is considered poor programming technique,
>academically, to have more than one return path from a
>function.

I completely disagree with this assertion (academically and practically).

Ever since I started using escape clauses in my code, the level of nesting in all my functions basically never exceeds two or three.  OTOH, striving for that "single glorious return" causes convoluted logic that must do a little song and dance to safely navigate out of the goddamn function when you have the "easy button" return!

Definition of escape clause (not meant to be conclusive proof of my claim, however):

// escape clause
function doSomething( handleA ) // escape clause
{
  if ( !handleA ) return false;
  handleB = handleA->getHandleB();
  if ( !handleB ) return false;
  $result = handleB->doSomething();
  return $result; // like to see this here for debugging
}

// no escape clause
function doSomething( handleA )
{
  result = false;
  if ( handleA ) {
    handleB = handleA->getHandleB();
    if ( handleB ) {
      result = handleB->doSomething();
    }
  }
  return result;
}
Derek
Wednesday, March 08, 2006
 
 
> I completely disagree with this assertion (academically and practically).

Agree. You just end up with a lot useless if blocks. With C++ destructors can handle all exit paths cleanly.
son of parnas
Wednesday, March 08, 2006
 
 
LOL.  I never said I agree with the "only one return path."  In fact, I despise it.  However, when you are teaching students, you have to teach them the way the professors want to see it; otherwise they don't do well and you lose your business.
newby
Wednesday, March 08, 2006
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz