[plt-scheme] Re: some philosophical musings on debuggers

From: Vadim Nasardinov (el-vadimo at comcast.net)
Date: Fri Sep 17 13:40:21 EDT 2004

On Thu, 2004-09-16 at 14:35, Eric Kidd wrote:
> 3) Debuggers are essential when debugging somebody else's code,
> especially if there's a great steaming mass of it.

This and other points have been made in this thread:

I extracted some of the more salient and/or entertaining comments.
My notes may be a little disorganized, but I have neither time
nor desire to clean them up.  See attached.


-------------- next part --------------
-*- outline-layout: (-1 : *) -*-

# For the above variable to be useful, put something like this in your
# .emacs:
#  ;; http://www.emacswiki.org/cgi-bin/wiki.pl/AllOut
#  (require 'allout)
#  (outline-init "ask")

# Author:  Vadim Nasardinov
# Since:   2003-12-02
# Version: $Id: debugging.txt,v 1.6 2004/09/17 17:36:47 vadim Exp $

* Intro
  In his Nov 29, 2003 blog entry titled "Debuggers are a wasteful
  timesink" (http://www.artima.com/weblogs/viewpost.jsp?thread=23476),
  Robert C. Martin writes

    ...for all their power, debuggers have done more to damage
    software development than help it.

    Since I started using Test Driven Development in 1999, I have not
    found a serious use for a debugger. The kinds of bugs I have to
    troubleshoot are easily isolated by my unit tests, and can be
    quickly found through inspection and a few judiciously placed
    print statements.

    In the ensuing discussion
    two camps can be identified.

    Those who agree with Uncle Bob tend to argue that TDD is a better
    way to develop bug-free software.

    Those who disagree point out that most programmers do maintenance
    rather than from-scratch development.  Given a large existing
    system that needs a-fixin', a good debugger may often be the
    fastest way to pinpoint the problem.

    My impression is that those in the second camp understand the
    first camp's argument, when properly scoped to where it actually
    applies, i.e. during development.  The first camp does not seem to
    understand the counterargument.  They don't seem to have come to
    grips with the concept that 90% of programmers do maintenance of
    stuff they haven't written, not development.
* Cons of using a debugger
.. Debugging sessions are transient.
   On the other hand, code instrumentation with logging statements is
   Possible fix:  scriptable debuggers.
.. Debuggers encourage sloppy code.
   Use case: you write a goodly chunk of code first, then try to get
   it working by stepping through it in a debugger.  Could be rather

   Possible fix: don't do it.
* Pros of using a debugger
.. You can debug code for which you don't have the source,
   e.g. third-party libraries.
* Quotes
.. Manish Jethani
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=0&msRange=15

    > Since I started using Test Driven Development in 1999, I have
    > not found a serious use for a debugger.

    I'm sure, because you're doing development, not debugging. I
    hardly ever used a debugger, if at all, when I was writing my own
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=30&msRange=15

    You sound like the programmer who writes code, and I happen to be
    the guy who has to debug your code 5 years later. Am I supposed to
    "wish" you had written better code? Nopes, I have a job to do, so
    I get on to just the right tool, the debugger, because someone out
    there has a business (or a hospital, or a bank, ...) that depends
    on the buggy software you wrote 5 years back.

    Also, software outlives "good coding pratices". What was good a
    few years back is considered bad taste now.


    What you really mean is, "don't jump on to the debugger too early,
    because I've myself wasted a lot of time doing that." Yes, one
    needs to know how and--more importantly--when to use the debugger.
.. Ashley Herring
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=45&msRange=15
    * The term "Debugger" is a bit of a misnomer, because it is also
      an interactive learning tool for exploring the way a program
      works. Hence, more often than not I've fired up a program in a
      debugger, not to debug, but to understand how it works. This is
      particularly important when you inherit somebody else's

    * "You can understand code just by reading it". Bullshit. Quite
      simply, utter bullshit. Why? Code describes the static structure
      of a process. A debugger allows you to look at the dynamic
      behavior of a process in action. No matter how smart you think
      you are, the number of states a non-trivial program can be in is
      vastly more than the mind can comprehend by "reading the
      code". How often does a innocuous looking method turn out to
      have a bug in it that only occurs as a result of an seemly
      unexpected sequence of inputs? Looking at the code, you don't
      even consider analysing how it would have behaved in that
      situation, because it "will never happen" thinks the
      developer. Then you fire up the debugger and surprise, surprise,
      it *does and did happen*. Why? Because the developer is not the
      user, and will frequently overlook common input
      situations. Debuggers provide a way to understand what is
      occurring in these unexpected, but commonly encountered

      Keep in mind, a "bug" is rarely as obvious as finding something
      like an incorrectly assigned reference - ie, something an
      "experienced" developer will pick up through a code
      review. Typically a program "bug" is not a code bug, but a
      design bug that did not take into account a certain
      scenario. The code looks fine, and may be 100% correct for the
      purpose it was designed for, but the "bug" is that there is a
      scenario that the design of the code never took into account.

    * This above situation - programs behaving unexpectedly due to
      input unexpected by the developer, but in reality a common input
      in production/"the real world" - is why unit tests by themselves
      are not the answer. Unit tests are written by developers to test
      the *expected* behavior of their programs. Unit tests are a
      great QA & development tool - they improve the quality of the
      code, but they are not a 100% replacement for a debugger. Why?
      Programs do crash, and when they do, it's not necessarily
      because of bad design, or lack of unit testing, but because
      something unexpected has occurred - something so unexpected,
      there was no unit test for it.

    * Unit tests are exactly that: a test on a self-contained
      unit. Programs are made up of a multitude of interacting
      units. The number of interactions in any non trivial system is
      so large that for it is a practical impossibility to write unit
      tests that consider every possible scenario.

    * Programs crash, have bugs, run slow etc, not because a developer
      has deliberately coded them to behave like that, but because the
      actual way the program behaves does not match the conceptual
      model the developer has of how the program should behave. A
      debugger provides a way of verifying that your conceptual model
      matches the actual behavior of the program.

   Whilst I agree with the original poster that debugging can be a drug
   and a lazy first stop analysis, I disagree that it's use should be
   discouraged. If a problem occurs, I tend to analyse the code, form a
   hypothesis of what is going wrong, then fire up the debugger to test
   that hypothesis, *before* engaging in any extensive refactoring. The
   idea of rewriting the "offending" code before running the original
   code through a debugger is just ludicrous - what if what you think was
   the "offending" code was actually not the real problem. Too bad, you
   have just spend 4 wasted hours recoding the wrong code, when 5 minutes
   with a debugger would have verified if you had correctly identifier
   the problem.
.. Michael Feathers
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=45&msRange=15
    > Refactoring does not catch bugs. Refactored code is
    > functionally identical to the code started with.

    It is deeper than that. When you TDD you end up with incredibly
    decoupled designs. It is one thing to talk about decoupling when
    you design, yet another to end up with one. If you want to see
    whether a non-TDDed design is decoupled try to write a test for an
    arbitrary class. It is usually harder than you might imagine.

    When you can get each class into a test harness, it is pretty easy
    to check for bad behavior, you write tests to nail it down. Far
    easier than working with an application that is a monolithic slab.
.. Greg
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=60&msRange=15
    In an application that uses libraries or is a client server app
    talking to something that you didn't write inspecting your code
    will not do any good. I've seen a lot of times when upgrades have
    broken perfectly functional code because of changes in libraries
    and api's that could only be identified with the use of a
* Debugging Scandal (CACM, Apr 1997, vol. 40)

  Introduction to the Special Issue on the Debugging Scandal
.. War Stories

   /My hairiest bug war stories/
   Marc Eisenstadt,  Open Univ., Milton Keynes, UK

     "Ada Kills Hairy Bugs" by John W. McCormick

   After several iterations of summarising the data, it became
   apparent that it would be necessary to say something about (i) /why
   a bug was hard to find/ (which might or might not be related to the
   underlying cause), and (ii) /how it was found/ (which might or
   might not be related to the underlying cause and the reason for the
   difficulty) in addition to (iii) /the root cause/ (what really went

   Regarding techniques for bug finding (second dimension), Katz &
   Anderson [5] reported a variety of bug-location strategies among
   experienced Lisp subjects in a laboratory setting involving small
   (10-line) programs. They distinguished among (i) strategies which
   detected a heuristic mapping between a bug's manifestation and its
   origin, (ii) those which relied on a hand simulation of execution,
   and (iii) those which resorted to some kind of causal
   reasoning. Goal-driven reasoning (either heuristic mapping or
   causal reasoning) was predominant among subjects who were debugging
   their own code, whereas data-driven reasoning (typically hand
   simulation) was predominant among subjects who were debugging other
   programmers' code.

   An alternative characterisation of bugs was provided by Knuth's
   analyses [6], which uncovered the following nine
   (problem-independent) categories: A= algorithm awry; B= blunder or
   botch; D= data structure debacle; F= forgotten function; L=
   Language liability, i.e. misuse or misunderstanding of the
   tools/language/hardware ("imperfectly knowing the tools"); M=
   Mismatch between modules ("imperfectly knowing the specifications",
   e.g. interface errors involving functions called with reversed
   arguments); R= Reinforcement of robustness (e.g. handling erroneous
   input); S= surprise scenario (bad bugs which forced design change,
   unforeseen interactions); T= Trivial typo.

   The informants reported four major bug-catching techniques, as

     - gather data
      o step & study
      o wrap & profile
      o print & peruse
      o dump & diff
      o conditional break & inspect
      o specialist profile tool
     - "inspeculation" (inspection, simulation, speculation)
     - expert recognised cliche
     - controlled experiments

.. Debugging and the Experience of Immediacy
      David Ungar, Sun Microsystems
      Henry Lieberman, Massachusetts Institute of Technology, Cambridge
      Christopher Fry, PowerScout Corp., Boston, MA

   The separation between cause and effect can occur in at least three
   dimensions: time, space and semantics, thus designers can strive
   for three kinds of immediacy that are important for debugging:
   temporal immediacy, spatial immediacy and semantic immediacy.

   Many C++ systems take minutes to change class definitions in a
   large program, forcing the programmer to waste mental effort
   devising workarounds, or leading to either boredom (leaving the
   flow state) or distraction (reading e-mail, etc.).

.. Software visualization for debugging
   Ron Baecker, et al.

   LogoMedia facilitates the visualization of programs with sounds as
   well as images. Connected to a MIDI synthesizer, it can be used to
   orchestrate acoustic feedback (Kramer, 1994) that informs
   programmers of key control and data flow events. Auralization has
   been used in software visualization (e.g., Brown and Hershberger,
   1991) to augment, enhance, or replace graphical or textual
   portrayals for several reasons:

* Links
.. Working With Legacy Code (a book in progress)
.. Linus doesn't like single-stepping

   I don't like debuggers. Never have, probably never will. I use gdb
   all the time, but I tend to use it not as a debugger, but as a
   disassembler on steroids that you can program.
* Bibliography
.. Knuth, D. E. The Errors of TeX. Software-Practice and Experience,
   19, 7 (1989), 607-685.

Posted on the users mailing list.