Circa 1999, one would assume that Brian Kernighan and Rob Pike are largely drawing experience from working with C, which is a relatively verbose language. Single stepping through C code in a debugger is indeed a laborious process.
If you read accounts from Smalltalk developers, on the other hand, it's clear that they very nearly live their entire lives inside the debugger, and consider it to be an enormous productivity booster.
I would guess that there are several effects going on there. One would be that, in terms of how much effective work it accomplishes, a line of Smalltalk code is generally not equivalent to a line of C code. That has a big impact on just how many steps are involved in single-stepping your way through a region. The other is the nature of the debugger itself. Gdb and Smalltalk debuggers are wildly different pieces of software.
The core of his position (as I understand it) was that regularly needing a debugger is a sign that your software has "gotten away from you". You've let the software get to a state where it cannot easily be understood from the architecture and program text alone.
I do think debuggers can be useful when building up comprehension - particularly of other people's software.
It's very time consuming though, and at each "trace" you're only seeing one path through the program. Good architecture and documentation lets you understand all the possible paths at once.
If it's being used too often then it points to the fact that the software has to be run in order to understand it. It's representation is not sufficient to convey it's run time behaviour.
Also would like to point that dynamic languages in general require more debugging than statically typed one's since one can't be certain of the data flow within functions.
In such a case it's difficult to decide what to print or more particularly how to print it if it's type/shape is unknown.
In those situations, it's often just so much easier to set a breakpoint and take a peek than it is to waste brain cycles on thinking about what exactly you should even be printing in the first place.
We need more tooling to help people understand and mitigate necessary complexity, not tools that help one muddle through or — I shudder to think — extend complexity.
I’ve changed my mind on this recently only because some IDEs have indeed become good at the latter.
Rather they do live changes of their code, and then evaluate various objects to verify that things work as expected. That how I seem to remember working in Smalltalk many years ago.
I am not a fan of debuggers, although I do use the REPL a lot. I suppose like Rob Pike, I used them only for very limited tasks, such as getting a stack trace or getting some sense of control flow. But as soon as I have that, I spend more time looking at code and reasoning about it, than stepping in a debugger. With a REPL I can try out assumptions I make about the code, rather than being forced to step through it.
You can also make a weird sort of UI this way, where the way you interact with your program is by changing the code / and or state while it's running. Breakpoints prompt for input.
Uh, me too. That's why I don't single-step through huge chunks of a program.
I use code breakpoints.
Add to this a need to build a debugging-enabled version of the project - an often long-running process, compared to a few edits to some 'release' build.
On the other hand, when dealing with an unfamiliar or complex and well-forgotten project, debuggers become that discovery and exploration tool that offers a wider context and potentially better situational awareness.
Of course, mix-in some concurrency and either debugging approach can equally become cumbersome without proper understanding of the project.
Using the programming language itself, I can extract exactly the information I need to see, transform it into exactly the shape that's easiest for me to inspect and combine output from different places in the code to create a compact list of state changes that's easy for my eyes to scan.
What I have found is that my debugging problems are either too simple to require anything more than thinking or too data dependent for a debugger to be the best tool for the job.
Rather then stepping through a program. Add breakpoints and just step from breakpoint to breakpoint.
With a good IDE, adding a breakpoint and hitting a shortcut key is faster than a print statement and on the GUI IDE your debugging sessions are not transient.
The only time their advice makes sense to me is when I'm in an environment without a gui. Even then jetbrains has a "ssh full remote mode." However at my company I have found that this feature doesn't work under Nix (nix-shell) so we all just use print statements.
> As personal choice, we tend not to use debuggers beyond getting a stack trace or the value of a variable or two. One reason is that it is easy to get lost in details of complicated data structures and control flow; we find stepping through a program less productive than thinking harder and adding output statements and self-checking code at critical places. Clicking over statements takes longer than scanning the output of judiciously-placed displays. It takes less time to decide where to put print statements than to single-step to the critical section of code, even assuming we know where that is. More important, debugging statements stay with the program; debugging sessions are transient.
I found this lines up with my personal experience. I used to lean on interactive debuggers a lot, and still enjoy using them. They're fun and make for good exploring. But the act of figuring out where you want to print really makes you think in ways that interactive debugging cannot. I find the two forms really complement each other.
[0] https://logging.apache.org/log4j/2.x/manual/index.html