Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: Do You Use a Debugger?
47 points by alfiedotwtf on Aug 21, 2021 | hide | past | favorite | 102 comments
It's been years since I've used a debugger (Turbo Debug), never bothered to learn how to use a debugger for non-assembly code, and tend to just sprinkle `print` everywhere until I can narrow down the problem.

I'm a Vim person, and am envious of IDE users with integrated breakpoints, stepping, and variable inspection with just a mouse, but only today found out about Vim's `termdebug` :headdesk:

So I'm wondering... what does the HN crowd use to investigate code problems:

    - sprinkling print
    - IDE with integrated debugger
    - termdebug (i.e editor with gdb in another pane)
    - debugger by hand on the command line
    - other (please comment)



I use debuggers a lot, although I'm a special case because I also build debuggers. But I became a debugger builder because I was an unsatisfied debugger user (as Mozilla developer working on Gecko, in particular).

A traditional forwards-in-time debugger like gdb sometimes beats print debugging, but often not or not by much. OTOH a reverse-execution-capable debugger like https://rr-project.org (disclaimer: I'm a maintainer) enables much more powerful debugging strategies like "set a data watchpoint and reverse-continue to where the value was written", which trumps print-debugging strategies in many cases (e.g. memory corruption). I use rr a lot to debug my main project, Pernosco. FWIW Pernosco is written in Rust, and it does have a fair bit of logging built into it, but I still find rr very useful there.

Pernosco itself is a new kind of debugger (see https://pernos.co --- let me not be uncouth by promoting it too hard here). I don't use Pernosco to debug itself that much, partly because that's challenging for technical reasons, but also because it is certainly true that debugging tools are more valuable for large complicated projects you don't fully understand and Pernosco is not such a project (yet).


Yes, and not just for debugging. Debuggers are great for learning a new codebase.

For debugging proper, sometimes it is indeed faster to just use print statements. As with everything else, you have to use your judgement.

If you are in a situation where you would like to inspect the flow of control through a large chunk of code, debugger is the best. How many print statements are you going to write? Twenty? Hundreds? Okay the log shows that some invariant somewhere broke, but now what? How did we actually get here? Of course, at some point using the debugger makes more sense in such a scenario.

The biggest problem I have seen people have with debugger (in C++ world) is that the debug builds run much slower and it can sometime takes hours to reproduce the state you want to debug. The tip I usually give is: you don't have to have the whole project be built without optimizations. Just enable those flags for the specific components you are debugging.


One gdb trick I used:

   Use simple gdb script,  break, print vals, cont, 
   This allows one to debug and app's internal states without rebuilding with printf enable. 
   Combine the above with automated regression test system, one to debug very hard problems that might take hours/days to repro.


> Yes, and not just for debugging. Debuggers are great for learning a new codebase.

I do that a lot. It is, for me, the best way of learning something I do not know.


It's especially useful with unfamiliar frameworks where you don't even know where the entry point is for a particular piece of code.


Yes, and it saves me hours every day in javascript and python. I'm amazed how few people are taught debuggers along with code. I really think that the first thing that should be taught to new programmers is a scripting languge in an environment with tab completion and a debugger. This way they would learn to code rather than learning to memorize runes and search for copypasta.


I think programming with a debugger should be thought towards the start of the CS course. It's been an immense help with grokking for loops and the like.


The first tasks with debugging are reproducing the issue and then isolating the problem area through process of elimination.

I read the code and think through it to form a hypothesis, then use print/error logging to test the hypothesis and come up with a fix.

I haven't used a real debugger in a long time.


I try to use a debugger whenever I can. Unfortunately, I develop on Linux, so the debugger situation here is awful. Basically almost all debuggers are based on gdb or lldb in true Unix fashion parsing text to generate a GUI... They tend to break really fast IME, since text doesn't provide good stability/reliability guarantees (no type checking, no nothing) and even when they do work, there is a lot of visual delay in the feedback loop, which makes them a pain to use.

Another problem is that often the debug builds of things at work run too slow to be practical, or they're too big to fit on the target device. My use of debugger at work usually boils down to getting and translating stack traces from core dumps we receive (on command line). There is a very rare occasion that I can do something on my local machine and then I use a debugger. Life is too short for 15-45 minute add logs + compile cycles. I also use bpftrace whenever I can.

At home I write Rust, but IME templates don't play well with the debugger, so I usually fall back on prints and bpftrace again.

But generally I'm convinced that using debuggers could be a big productivity boost. Here[1] you can see a demonstration of how smooth working with a debugger can be, in this case it's RemedyBG. But forget about this level of comfort on Linux. The whole video may be also worth watching if you like rants and want to see how Visual Studio debugger quality dipped over the years (first the current VS debugger is shown, and then an old version (from VS6 IIRC)).

[1]: <https://youtu.be/GC-0tCy4P1U?t=1845>


C++. I have decades of experience with gdb, but in practice I almost always prefer to use prints. I think this is because my particular development environment makes prints really, really effective.

My build system automatically rebuilds my code and reruns all tests immediately upon me saving a file. So I sit with the test output open in one window and the code in another. I add a print statement, save, and a few seconds later, I see it in my output log. No need to even move focus out of my editor frame.

I find that for most purposes, this is much faster and more powerful than trying to use a debugger. I don't have to switch workflows between code editing and debugging. I get to probe the state from anywhere in the execution timeline, not just one breakpoint at a time.

Of course, this requires that whatever I'm debugging is reproducible in an automated test that runs fast (within seconds). This approach doesn't work nearly as well for anything that requires interacting with the program. But I mostly work on lower-level infrastructure and libraries that are highly test-driven and non-interactive, so it works out.

A few superficial things that help this workflow:

- I have a dedicated macro, KJ_DBG(), for debug prints, so I can easily stop myself from accidentally committing debug prints.

- The macro is optimized for debugging. If I write `KJ_DBG(x, y, z)` I'll get a log message saying "x = <value>; y = <value>; z = <value>" -- no need to waste time writing a format string.

- `KJ_DBG(kj::getStackTrace())` dumps the call stack trace, `kj::getAsyncTrace()` traces async event callbacks, etc.


Yes all the time, debugger for JavaScript and binding.pry for ruby. I don’t understand how people can rely purely on print debugging. I find it much more useful to stop and inspect the state and input, and being able to print a backtrace from that point is also valuable.


I rely only on print debugging purely due to lack of investment in my tooling, both by myself and my work environment.

The thing is, it always just works. I don't need to think about it. I write code in 5+ languages fairly regularly, with some frameworks that have really weird... "support" for dev tools.

But maybe I am just a bad developer with bad excuses. If anyone has been in my shoes and seen the light, would love to hear how you manage jumping in to multiple different languages and keeping a consistent debugging experience (or, at least, more consistent than print).


It really isn't possible to have a consistent cross platform experience outside "closed ecosystems" like the JVM. The problem is that good debugging usually has to integrate very deeply with the language/ecosystem/hardware. Unfortunately programming languages are a mishmash of distinct philosophies implemented differently by people who largely didn't agree with one another, so the debugging experience in most of them is inconsistent and because experience transfers less than you might think, generally abysmal. Moreover, all that deep integration and complexity mean that debuggers are sometimes wholly inappropriate for particular situations and as the use you'll just have to know/learn when that is.

But if you can get your hands on a "good" debugger with (e.g.) time travel or reverse debugging, there are entire classes of issues you can instantly solve. It's like having superpowers.


use the best tool for the job. vscode has some ok debugging features for some scripting languages. some of the eclipse backed things like pycharm or jetbrains also has some decent debugging baked in. Anything c/c++/c#/java/swift/kotlin use the appropriate IDE for that(visual studio has really good debugging, xcode is ok, android studio is ok).

for 80% of the work I'm doing lately it's vscode + LSP + debugger + neovim as a backend.


Now that I think about it, debugger and logging are orthogonal tools:

  Debugger: show me everything at this point in time
  Logging: show me this thing at every point in time


Why not both?

It is possible to build a debugger that does both; Pernosco does. (Disclaimer: my baby.) https://pernos.co/about/basics https://pernos.co/about/expressions


The Jetbrains editors (IntelliJ, Webstorm, etc.) all have tremendous debugger support, which I use.


I sprinkle printf's OR use my 'xlog' logging function which can be configured to use more restricted parameters.

Mainly I use the 'xlog' function. I can then wander about the logging file (using a text-editor for navigation) looking at various 'befores' and 'afters'.

I use a bit-mapped variable 'debug' which allows or disallows the logging statements accordingly.

For example, in an emulator, with debug only configured for floppy-disk-controller logging, and looking at the floppy-disk controller code, we might see:

      xlog (FDC,
            "sdma_getdatareg: disk_data: fd_byte_ptr:%04X  bytes_to_xfer= %4X data:%02X\n",
            fdcd->fd_byte_ptr, fdcd->bytes_to_xfer, data);
which would be logged, and

     xlog (INFO, "RESETTING %s\n", MACHINE_NAME);
which would not.

As for using 'gdb', I tend to use that only when I have no clue, or a wrong clue, where the code is breaking down.

I tend to leave the logging code around if it's not particularly obstructive or long-winded as I can switch it out at will by setting 'debug' to zero.


I really like when software uses this technique and leaves the logging present in release code. As a sysadmin being able to crank up debugging logs is nearly as useful as source code access if the developer has been thorough with their logging calls. Seeing internal state goes a long way toward figuring out whys seemingly-correct configuration isn't actually doing what you want.


I can do the same in the debugger without changing one line of production code thanks to tracepoints, and breakpoint actions.


Yes you can, but you need to be pro-active about what you want to know when using gdb (etc.) that way.

Logging gives a solid background of stuff you may not have thought about specifically. It is often the first clue that 'something is rotten in the state of Denmark' when the values that show up are not what they should be.

In truth, debugging is a detective-story, and anything that helps, be it debuggers or logging or printf, is grist for the mill.


Most of the logs I see in production are useless garbage because the coder did not thought of what is relevant.

With a debugger I can place the probes and do my own printfs instead.


<grin>

It's not one or the other. You are free to use both, logging and a debugger.


Print statements for small one-off programs.

IDE with integrated debugger for larger programs (mostly C# and some Java experience here).

gdb (or similar) for programs without an IDE and larger than a small one-off program.

Some languages also lend themselves (or their implementations do) to better debugging experiences. Common Lisp debugging, for instance, is almost a joy. For grins, I implemented the VM for the Synacor Challenge [0] in tandem with SBCL's debugger. After parsing the opcode, the program entered an ecase. If there was no entry for the opcode, it brought up the debugger, I extended the program, recompiled whatever needed to be recompiled, and resumed it. Wash, rinse, repeat. Once every opcode was implemented it then went on to run the image and I could finish the challenge.

[0] https://challenge.synacor.com/


Do you use plain terminal gdb or some sort of front end over it? Gdb is extremely powerful but UX is abysmal, I wish there was some interactive GUI that would expose most of its functionality in an interactive and intuitive way.


Plain gdb, or its TUI on occasion.

EDIT: I originally wrote this on my phone and while in a bit of a rush. More relaxed today.

I learned plain (CLI) gdb in college, it was taught as part of an exercise in the 3rd CS course and I found it useful during the course. Though I also still largely used printf debugging back then. At some point I had to debug embedded systems that I could connect to via a serial terminal that had gdb running on them. A GUI was not feasible, so I really learned gdb at that point and found I remembered it well enough to not need another interface. But yes, a GUI would be nice especially if it's a tool you use infrequently (like it would be for me now, I don't do much in the way of professional C or C++ programming that would benefit from gdb knowledge at this point, or not on systems where it can be used).


I often sprinkle print statements around, but would also like to point out https://rr-project.org, which takes gdb-level debugging to a whole new level.

E.g. you hit an assertion in your code because some value was outside the expected range? Fine, set a watchpoint on it when the assertion fires, and simply reverse-continue to see where that value came from. It could change your debugging life...!


In my experience, people who champion printf debugging usually don’t have the option to use an effective debugger. Low-tooling embedded or distributed systems for example.

Many others champion “you should understand your own code well enough that you don’t need a debugger”. Those people are usually in an unusual situation where they can work on a small bit of code in a small/solo team for a very long time.

Meanwhile, much of my career has been spent stepping through code I’ve never seen before and will never see again trying to fix bugs that span multiple teams and external libraries.


Not since 2009. I simply put print statements on strategic places, and through a process of elimination I find the issue 99% of the time. Most of the time I never go beyond 15-30 minutes.

Sometimes it takes hours, sure, but I'm not seeing how a debugger would accelerate that (since in those cases I have to wrangle a lot of external state like DBs and VMs/containers). Debuggers are a pain to learn and utilize to their full capability.

Am I missing out? I'm not saying that I'm not but haven't practically felt the actual need for a debugger in a long time.


Maybe depends on the case.

If you need to track down bugs whose „lead time“ are not in seconds/minutes but hours, you‘ll need a debugger. E.g. a bug only happens after some previous long-running task is run.


Oh, absolutely. But to me writing programs that way is a malpractice :D so I strive very hard to not have code and/or whole programs acting like this (long lead times as you said). So I break it down on smaller chunks and cover those with tests, extensively.

Definitely not a bullet-proof strategy but it gets me 90% of the way. The rest 10% I work hard to cover with telemetry / logs / "debug" print statements.

I mean don't get me wrong, I've done some pretty amazing things with `gdb` a loooooong time ago but gradually, as my career took a sharp turn mostly into backend web development, I lost the need for a debugger.

Since I am looking to branch out of web development again and likely land in Rust / OCaml lands, I'll probably need to relearn debuggers.


I'm just not that good a programmer, but I still feel the need to program so I tend to take print debugging to absurdist levels, if I start a project that i anticipate is a stretch for me (not being that smart), I make sure that debugging strategies are there from the start.

One particular knotty routine for me was a small synth, I knew I had neither the math ability or c wizardry to develop complex things easily so I made sure there was plenty of scaffolding to assist me. For each 1k of audio output, there is easily 1m of debugging output that I would then "query" with perl, pagenate each step that I could page through, etc, debugging was put in from the start.

Allowing flexible debugging strategies that I could adjust for the difficulties at hand just seems right for me and I find often that different programming constructs need different debugging strategies, and I just don't think debuggers are agile enough to let me adjust.

Its really that I'm just too dumb to get benefits from debuggers, although I'm guessing they may be just perfect for folks smarter than me.


Stop downplaying your own abilities. My day job involves unfucking code written by people who think they’re smarter than they actually are. Write humble code, but stop thinking you’re dumb because of it.


If I'm confused why something is happening I'll set a break point and then use the debugger to see what the world looks like at that point. That's an advantage when something happens very rarely. One trick is to write a test to detect a problem and then put a break point on that. Let it run until the bug gets hit. Then look at the call stack to see who was naughty.

I use debug print statements to monitor running programs while doing end to end tests.

I have functions to log errors and restart programs in production. Any busfault gets trapped and logged as well. Often just knowing which line of code barfed tells you what happened. Often I also log the calling address off the stack.

I also have a table driven command line interface that I use heavily to poke at and inspect running programs. Having that probably reduces my need for a debugger. Unlike a debugger it's effect on a running program is minimal.

For timing related stuff and debugging bus interfaces I often use a four channel scope.


> I'm a Vim person, and am envious of IDE users with integrated breakpoints

When I started writing Rust some years ago I switched to using an IDE full time. Before that I had mostly been using Vim for the majority of the code that I wrote.

The IDE I use is JetBrains CLion, and I love it a lot.

Importantly, JetBrains IDEs have a Vim emulation plugin that you can install, so you still get to use many of the key bindings and features that you know and love from Vim, including recording macros and playing the macros back etc.

I really recommend that you check out the JetBrains IDEs. Install a trial version of the one(s) suitable for the language(s) that you use, install the Vim emulation plugin, and use it for a while.

https://www.jetbrains.com/


I rarely use a debugger. The experience is just awful in almost every debugger I've used.

For low level code where you really want to see all the program/function state evolving over time, they can be useful.

Your Java program throws some random NullPointerException? Prints, and lots of them. The experience with the debugger is miserable. I set a breakpoint somewhere, then do I jump or step or whatever? Who knows, all I know is whichever I pick is the wrong one everytime.

I saw another poster mention a debugger that lets you put breakpoints that print stuff. Now that would make me use a debugger all the time. Maybe the debuggers I have used already support that, I certainly wouldn't know if they do, they didn't make it discoverable.


Well, a debugger on a breakpoint should allow you to inspect any local scope variables along the globals, go through the call stack.. And you can watch vars, execute immediate commands and more in others. If that's not "print" on breakpoint...


I don't want to print on a breakpoint, that is again useless, the cause of the problem might be far removed from the location of the exception. I want multiple breakpoints, each of which prints data, so I get a log of the behaviour of the relevant parts of the program overtime.

Being able to stop the program at a single point does not help, not unless I can actively move backwards in time (not in the stack!) like in the Elm debugger (actually useful).


> Your Java program throws some random NullPointerException? Prints, and lots of them.

Or just tell your debugger to break on exception.


That's useless, debugging a NPE is about figuring out where the null originated. Breaking on the exception is too late.


java 14, helpful NullPointerExceptions? https://openjdk.java.net/jeps/358


I mostly code in TypeScript / Node / React.

My debugging tooling of choice is `console.log` when dealing with simple flows: Do an HTTP request and print the value of a variable

I find it lighter (and faster) than attaching a debugger and slowing everything to a halt.

For advanced asynchronous behavior I use Chrome Dev Tools + React Developer Tools + Redux DevTools with these I can inspect any value, profile, time-travel etc..

I'd say the ratio is 80% console.log / 20% dev-tools


In Python, I use the PDB/PDB++ debugger extensively. When you get a confusing or nonsensical error from deep inside an under-documented 3rd party library, sometimes stepping through that code with a debugger is the only way to figure out what mistake you've made, or what under-documented assumptions the library makes. I also use it a lot for diagnosing test case failures, especially in property-based testing when I might have accidentally generated something I shouldn't have, or otherwise can't figure out why a test is failing.

The experience is even nicer in the Pycharm IDE, where I can set breakpoints with arbitrary conditional logic just by pointing and clicking. It's so easy it almost feels like cheating.

That said, Pycharm is a heavy beast. Psychologically it's not worth the overhead of opening it or even switching projects within it unless I'm working on something involved for an extended period of time. So figuring out how to do something like this in Neovim has been on my to-do list for a while.

I also use the built-in Python logging framework to help me identify where problems might be happening in the first place.


FWIW, debugging python in VS Code is nice, also allows setting conditional breakpoints and watches. Nice middle of the road option between Pycharm and Neovim. Pretty much no setup, works out of the box after installing the python plugin.


Fully agree with debugging Python in VS Code. It's everything one can ask for.


Reading Coders at Work, I discovered that even legends like Donald Knuth, Peter Norvig, and Ken Thompson rely on print statements for debugging.


They are great for detecting whether you hit some code or not without having to use breakpoints and such.


Haha wow, OK thank you... This brings me comfort :)


Great question. It sounds like it should have an obvious answer (what kind of crazy person wouldn't use a debugger?!), but now that I think about it, there's many situations where I still use print debugging:

- In C++, setting up a (visual) debugger is often non-trivial, and the quality of debugging is naturally lower than in higher-level languages. So I tend to get along with print. Some bugs absolutely require the debugger though.

- With WASM I tried to get the Chrome debugger to function but gave up. Not sure it works yet.

- In Python, usually I am executing small scripts via the command line, outside an IDE, so I just resort to print statements.

To be clear none of these are my primary programming language. If I was spending significant amounts of time in front of a language I would give JetBrains however much money they asked for to get a real IDE and debugger.

Generally I don't find command-based debuggers to be that compelling, it feels like writing text with `ed`. When I do take the time to set up a visual debugger it is a significant productivity multiplier.


Back when I was doing C# development I would use the Visual Studio debugger all the time and was pretty happy with it. When I moved to doing Clojure and ClojureScript I decided to learn emacs and stopped using a debugger until I learned about the debugger in Chrome when using clojurescript dev tools (it will allow you to set breakpoints in your ClojureScript source in the Chrome Developer Tools console).

My technique for debugging with Clojure + emacs was to use a bunch of print statements and then also using a repl to try and duplicate the issues or at least see what was going on. Less than a month ago I started using the CIDER debugger inside emacs and have been happy with it overall. There are some issues I have had, namely that you can't set breakpoints in macros which is understandable. I have gotten around that by creating a temporary function and move all the code into that function. The main use case I have for doing that is when trying to debug Compojure routes, which are defined in macros.


I used to get by primarily with print statements and occasional use of gdb either on the command-line or with the Emacs gdb mode. Back when I used Emacs and vim and I was something of a Linux zealot I never really considered anything else as being worthwhile.

However, over the last few years I've switched mostly to CLion, which has excellent support for gdb and gdbserver, so you can use it to debug local, remote and embedded applications directly on the target with a debug probe. It can't be understated just how much of a timesaver a graphical debugger is compared to controlling things through the CLI. Stepping in/out/over, setting breakpoints and watchpoints, viewing the call stack and individual frames, and jumping back and forth from source code to disassembly. You can do all this from the CLI, of course, but it's much less efficient.

As much as I despise Eclipse, even it manages to have excellent gdb and gdbserver support which is also vastly better than the CLI for debugging.


Yes, I consider a step-debugger that's tightly integrated with the source editor for a seamless edit-compile-debug loop quite essential for development (not just for "actual" debugging). A debugger allows me to explore code (my own and other people's) from the "inside", instead of just observing its behaviour from the "outside". Of course I also use temporary print statements, log messages and various types of tests, those are all just additional tools in the development tool box.

Also, with a good UI debugger there's not much to "learn" since the UI is pretty much self-explanatory. You usually have two or three hotkeys for step-into, step-over and step-out, click with the mouse to set a breakpoint, and a few UI panels to inspect variables and memory. Everything else are advanced features that are useful to know, but not essential.


What GUI debugger(s) do you use/recommend?


Depends on what platform I'm on and what programming language I'm using, Visual Studio's or Xcode's integrated debuggers for C and C++.

Visual Studio Code has debugger plugins for almost everything, but the debugging UI is more bare-bones than (for instance) Visual Studio.


I don't use a debugger. When I have a problem that I need to narrow down I state my hypotyhesis of what is going on in some kind of unit test and narrow it down that way.

I am currently working on a Python/Django project of around 45k lines of code, 28k of which are tests. For me, this approach works quite well. It is not that I am against debugging or that I don't value debugging as a tool, but I just never feel the need to use one.

The tests also have a nice side-effect: because I do TDD a lot, I mostly end up with a lot of small understandable units, so I am effectively preventing code with properties that need me to follow each statement one by one.

I hardly ever feel the need to interrupt the running process to inspect the current variables. In many cases, this would be futile anyway, because I started writing a lot of generators.


Yeah, I use debuggers, primarily the integrated Visual Stufio debuggers. Also gdb and WinDbg, but I need to learn those better. And ofc the integrated debuggers in browsers.

I’ll also use print debugging if I feel the situation isn’t worth bringing in a debugger, primarily one I don’t know well yet (see above).


In code I have written, understand how things come together, how things control and data flow from one subsystem to another, I find I don't really need a debugger and print statements provide the same information. In these cases, I'll use whichever is easier to get going with; if stdout etc isn't immediately reachable, or recompiling is awkward then a debugger is useful. But sometimes getting a debugger running is harder than just rerunning a routine again with print/log statements.

On the other hand, when I'm working with a system that I don't know inside and out, a debugger is immensely valuable for helping me 'navigate'; to see what actually gets called when some interface/virtual method is called.

I live in the second world more often than the first.


In the past I have used debuggers when learning a new language. Helped me to understand what was going on. I'm too impatient to single-step all but the most trivial routines.

When faced with a new API or algorithm, I write small programs with lots of prints to test/understand the function/object in question. Once that's done I encapsulate as a module and retain some assertions to catch any abuses.

I then build systems using the pre-tested modules. At that point I rely on the assertions, logging and exceptions to identify the source of problems. Problems are quickly traced back to the module with bugs which then undergoes further polishing as above.


I use all the above and then some.

I've written code to print scripting language stack traces when native code segfaults.

I've written code to auto-collect crashdumps from coworkers, and add important globals (or what they point to) to said crashdumps when collecting full 32GB+ memory dumps are impractical.

In-process debug toggles might show graphs or annotations that provide much more intuitive and visual alternatives to printf spam.

Unit and fuzz tests help catch things before they manifest in the wild.

GDB/LLDB supports python scripts which let you customize the debug view of some of your more opaque types. MSVC has .natvis files, which despite being awkward XML soup, can also be quite useful.


I have recently started using a debugger (GoLand) again after years of just using print.

The cost of working out how to configure the IDE/debugger to set the environment and run the build commands was never worth it. But for debugging tests it’s great.

I think a working debugger is faster than adding print statements, but slower than debugging by inspection (ie running the code in your head). So I read the code and if I can’t work it out, I’ll add print statements, and if I’m still confused then I’ll work out how to set up the debugger for my environment, again.

But because there are two steps before I’ll break out the debugger, I don’t use it too often.


I think. I think about what the code is doing, what bug I am seeing, and what could have gone wrong to make it happen. I then watch whatever I need to to confirm/deny my hypothesis. Sometimes it is a debugger. More often it is a print statement. Sometimes it is the UI. Sometimes it is the database. Once in a while the network.

If I have no freaking clue why something is going on, I run through a debugger line by line and watch it all. But in all honesty, that is a rare case. It is far more likely that just slowing down and thinking what could possibly cause a result will guide you in the right direction.


Almost never. Logging is the only real solution in real-time distributed systems.


I used to program without a debugger back when I was a PHP developer, and I carried that habit into JavaScript because I had no idea browser dev tools existed. Once I found out I never looked back.


I have used a debugger, most recently on a VB.NET web application that someone else wrote and that I had to maintain. I could not have done without the debugger there. But I haven't touched the application in several years now.

Before that--almost twenty-five years ago--I figured out how to use the COBOL "animator" to find out why Peoplesoft payroll processing was crashing on us. There was no way in the world to have figured that out without the animator.

These days, though, I mostly use print or logging.debug


A vim user might be interested in using vimspector [1], which implements a debugger adapter protocol (DAP) client for (neo)?vim. It's actively developed.

Leveraging DAP lets you have a uniform interface for debugging in multiple languages. (DAP is a sister protocol to the Language Server Protocol which abstracts IDEs from debugger services)

[1] https://github.com/puremourning/vimspector


Working in a .NET shop, I use Visual Studio with its integrated debugger. It's pretty powerful, but 99% of the time I could probably use print debugging just as well.


I better not have to, but I'm glad I learned how and still spending 20 min going over how to use the one in pycharm during new eng onboarding.

Usually if I need to break out the debugger, the flow has already become out of control in complexity to the point where print doesnt even help anymore and needs a refactor anyway, but the bug needs to be fixed sooner than the 1-2 weeks that would take. I also tend to find flaky test fixes faster using the debugger.


1: On rare cases i use print (testing new stuff), in most case i'd rather have a logger (or the poor man solution : `if $DEBUG == 3: print X`, but a logger is better): it allows me to keep my debugging structure to understand failure (especially when i get the input from a source i don't manage)

2: I wish, but most of the time i can't run in on my company laptop, so no.

3: Yes, pdb/gdb every time

4: With lisp (but really, it's like a step-by-step debugger, so no?)


Sometimes I use a debugger. Sometimes I use print statements. It depends on the task. If it's a loop that runs multiple times, I lean toward using a debugger. If stepping through the code would be too much hassle, then I lean toward using print statements. If I need to explore the environment (often due to poor documentation), then I lean toward using a debugger. In short, I use whatever seems to be the most effective approach.


I'm trying to get myself to use debuggers more often, but I find that just modifying the code to print stuff, then inspecting the result, allows me to better figure out what went wrong. Say I have a small sample data set where I expect a loop to run three times, but it runs four times. It's really easy to see in the printout, but counting the steps in the debugger is tedious.


> counting the steps in the debugger is tedious.

that's a debugger UX problem - the debugger should have a way of setting conditional breakpoints, and allow you to evaluate expressions (and optionally print out expressions).

The only time i've found the debugger lacking is when you have to debug concurrent or async code, and the act of debugging changes the execution to either hide or present a different bug.


I have yet to see a debugger that counts the times a breakpoint is hit. But maybe rr and it’s ilk do.

I find it really valuable that I can just read a text file to spot some patterns even if I don’t know beforehand which patterns I’m looking for.


Visual studio debugger can track hit counts. I would be _very_ surprised if gdb can't do it as well.

With gdb you can even script what it needs to do when it hits a breakpoint. So even if it's not built-in, it's scriptable.

edit: You can even trigger a break or commands if a breakpoint hits a certain number of times: https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Breakpo...

So the hit count is definitely stored.


This is my favorite self-invented trick when I need a debugger to find the spot where my code is crashing but don’t have one handy or forgot how to use it: In vim, select a block of code, and then replace any blank lines with __LINE__ printfs. Run the program, and it will log until the point where the crash is. Add more blank lines and redo the replace if necessary.


Depends. I use debuggers like pdb when I can’t quickly figure something out as it lets me interact with the running state. I’m less likely though in languages like Golang where I haven’t had as easy of a time using one, and have ended up avoiding them often in UI work as I more often end up in frameworks than code I care about


This all time classic describes my stance pretty well: https://lkml.org/lkml/2000/9/6/65. Although I don't need a disassembler that often so my use cases are even less.


All of the above, depending on the situation. Haven't found one set of tools that combines the best of each.


Of course. I use a debugger but I also use print statements. Using a debugger can be great because you can change values on the fly and explore in different ways.

If it's available I would question not using one. If I was pairing with someone who wouldn't use one or didn't know how I would be frustrated.


Intellij IDEA for Java code debugging; the only time I need to sprinkle log statements is when compiled code runs inside a Docker container.

When I go back to frontend code, I will definitely use a debugger in the browser, unless it is a super heavy client riddled with performance issues (and even then, probably).


I once was given a chip emulator debugger. Complete hardware level control! I discovered that it was faster to debug with semaphores and careful thinking.

Later I was the test manager of Turbo Debugger at Borland.

Today I almost never use a debugger. It is my absolute last resort. I print to the console, though.


Test manager of Turbo Debugger?!

Wow, that's awesome. Let me thank you for helping me early out it my life... to be honest I used Turbo Debugger more to learn programming (editing the instructions and running them, rather than using the assembler) than debug itself!

What a wonderful tool it was!


Yes, all the time.

Personally I think most people never get taught debugging tools properly, so they get to use like 10% of their capabilities and then think down of debuggers.

Visual Studio team has lots of tickets where asking for feature X, they get closed with already available.


As a system/DevOps engineer I rarely write code complex enough to need a full blown debugger.

Most times either a print statement or the equivalent of an assertion will be enough to spot the problem.

Some stuff need to be coded in python though, and then sometimes I use pdb.


gdb and rr are very useful for navigating a large, unfamiliar C and C++ codebases. Since these languages don't dictate how code is organized, the debugger can be the best tool for guiding you through the relevant code for your problem.


I often use a debugger for code I cannot easily recompile, like glibc or Rust stdlib.


A debugger is only useful for code that you don’t understand.

If you’re writing code that you yourself don’t understand then a debugger isn’t going to get you much, except maybe if you’re using it as a kind of imitation REPL to learn the language.

If you’re dealing with code that someone else wrote then it can be a bit more useful, since with large codebases it’s not always practical to fully understand everything. However, it’s liable to give you a false sense of understanding, so some humility is warranted.

In fact, I want to make the point more strongly. Operationally reasoning about program behavior by stepping through a specific execution is like doing analysis using a jar and a pile of pebbles. It will work in the simpler cases, but is utterly useless for nontrivial algorithms, including any that involve concurrency or nondeterminism.


> A debugger is only useful for code that you don’t understand.

The problem might not be with the code but with the data the code is operating on. To give you a specific example. Suppose you're rendering something and at a certain point it displays some weird artifacts on the screen. You could go over every draw operation the renderer performs to check if there's a mistake in the code somewhere. Or you could check the thousands of points in the data to see if you can spot the mistake there.

My experience is that in this specific case, it's a lot faster to just debug and trace the program. The debugger can then display the current rendering surface each time you break into it. You'll find out which draw call caused the artifacts and which part of the data it was trying to render.

In some rare cases, you'll discover neither the rendering code nor the data was bad. It looked strange because the data was strange but not invalid or wrong.

The point I'm trying to make is that it's also about speed. You use the tools which gives you the fastest results. Sometimes it's quicker to read the code and reason about it, sometimes it's quicker to write printf's, sometimes it's quicker to use a debugger.


Valgrind or -fsanitize=address/undefined usually do the trick. The usual printf is fine otherwise. I read somewhere that a generic PRINT macro is in the works for the next C standard. Might be of use


Depends for what purpose. High level code? Never, just printf, or some form of dynamic tracing.

Low level code? All the time, and only at assembly level (even if I am debugging C).


Very, very rarely I will drop into ipdb or use gdb. The rest of the time I just use print statements if I can't figure out an issue by inspecting the code.


Combining some form of automated testing (doesn’t have to be anything fancy) with a debugger provides a very fast feedback loop you can iterate quickly on.


I use Visual Studio to trace through code I’ve written recently to see if it’s doing what I meant it to. It’s like proof reading.

(Professional coder since 1987.)


Pdb and/or its integration vscode for python. Back when I worked with Groovy professionally I made heavy use of the debugger in IDEA.


Yes. I use PyCharm specifically for its debugger.


I live in the debugger. My .gdbinit file is huge. Logging is nice to get the hints, but in the end you need to be there inside.


Nope. I've tried a number of times; most debuggers I've used don't seem worth the complexity over printing etc.


prints don't give you an idea of the global state of an app at a given time, this can be tiresome to solve bugs like that, a debugger is essential. I use the classic stuff: gdb (with python-scripts and gef) for C/C++, ipdb for python, and `debugger` statements for browser and nodejs development.


Yeah it's easy to miss something obviously wrong with print debugging if there's something you're not printing (because "obviously it can't be that!")


Yes, all the time. Gdb or windbg, depending on the operating system I'm on at the time.


nothing better than some well place printfs

If anyone is interested gdb has a visual mode. Eg: gdb ./a.out

then while inside gdb ctrl+x, a

so press ctrl+x at the same time, let go and press button 'a'


lldb with a graphical interface. I grew up with graphical debugging interfaces, could probably use console based ones if i had to but never bothered to learn


I use the chrome debugger as my IDE. I have been using it to edit/run/debug javascript for years now.

My entire development stack is installed on any machine that runs chrome. It's awesome.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: