5 ms·
On the other hand, John Carmack loves debuggers - he talks about the importance of knowing your debugging tools and using them to step through a complex system
by johnfn 1y ago
On the other hand, John Carmack loves debuggers - he talks about the importance of knowing your debugging tools and using them to step through a complex system in his interview with Lex Friedman. I think it's fair to say that there's some nuance to the conversation.
My guess is that:
- Debuggers are most useful when you have a very poor understanding of the problem domain. Maybe you just joined a new company or are exploring an area of the code for the first time. In that case you can pick up a lot of information quickly with a debugger.
- Print debugging is most useful when you understand the code quite well, and are pretty sure you've got an idea of where the problem lies. In that case, a few judicious print statements can quickly illuminate things and get you back to what you were doing.
- 4hg4ufxhy 1y agoIt depends on the domain. Any complex long lived mutable state, precise memory management or visual rendering probably benefits from debuggers. Most people who work on crud services do not see any benefit from it, as there is practically nothing going on. Observing input, outputs and databases is usually enough, and when it's not a well placed log will suffice. Debuggers will also not help you in distributed environments, which are quite common with microservices.
- BlackFly 1y agoDebuggers absolutely help in distributed environments, in the exact same way that they help with multithreaded debugging of a single process. It is certainly requires a little bit more setup, but there isn't some essential aspect of a distributed environment that precludes the techniques of a debugger. The only real issue in debugging a distributed/multithreaded environment is that frequently there is a timeout somewhere that is going to kill one of the threads that you may have wanted to continue stepping through after debugging a different thread.
- strogonoff 1y agoIs there a name for an approach to debugging that requires neither debuggers nor print calls? It works like this: 1. When you get a stack trace from a failure, without knowing anything else find the right level and make sure it has sensible error handling and reporting. 1a. If the bug is reproduced but the program experiences no failure & associated stack trace, change the logic such that if this bug occurs then there is a failure and a stack trace. 1b. If the failure is already appropriately handled but too high up or relevant details are missing, fix that by adding handling at a lower level, etc. That is the first step, you make the program fail politely to the user and allow through some debug option recover/report enough state to explain what happened (likely with a combination of logging, stack trace, possibly app-specific state). Often it can also be the last step, because you can now dogfood that very error handling to solve this issue along with any other future issue that may bubble up to that level. If it is not enough you may have to resort to debugging anyway, but the goal is to make changes that long-term make the use of either debuggers or print statements unnecessary in the first place, ideally even before the actual fix.
- dgb23 1y agoIn order for this to cover enough space, I assume you‘d have to really pin down assumptions with asserts and so on in a design by contract style.
- PaulDavisThe1st 1y agoA different domain where debuggers are less useful: audio/video applications that sit in a tight, hardware driven loop. In my case (a digital audio workstation), a debugger can be handy for figuring out stuff in the GUI code, but the "backend" code is essentially a single calltree that is executed up to a thousands times a second. The debugger just isn't much use there; debug print statements tend to be more valuable, especially if a problem would require two breakpoints to understand. For audio stuff, the first breakpoint will often break your connection to the hardware because of the time delay. Being able to print stacktraces from inside the code is also immensely valuable, and when I am debugging, I use this a lot.
- fingerlocks 1y agoSeems to be the opposite for me. Usually I can pretty quickly figure out how to garden hose a bunch of print statements everywhere in a completely unfamiliar domain and language. But the debugger is essential for all the hard stuff. I’ll take heap snapshots and live inside lldb for days tracking down memory alignment issues. And print statements can be either counterproductive at best, or completely nonexistent in embedded or GPU bound compute.
- kibibu 1y agoIf using step-by-step debugging is a minor superpower, conditional break-points make you an Omega level programming superthreat.
- kibibu 1y agoI'm sorry, but this juxtaposition is very funny to me: - John Carmack loves debuggers - Debuggers are most useful when you have a very poor understanding of the problem domain
- johnfn 1y agoIf you listen to what he has to say, it’s quite interesting. He would occasionally use one to step through an entire frame of gameplay to get an idea of performance and see if there were any redundancies.
- kibibu 1y agoI really tried but could not take to Lex Friedman's interview style.
- rusk 1y agoI think you should substitute “code” for “domain” in the last paragraph. John Carmack knows his domain very well. He knows what he expects to see. The debugger gives him insight into what “other” developers are doing without having to modify their code. For Carmack, managing the code of others the debug environment is their safe space. For Kernighan et al in the role of progenitorous developer it is the code itself that is the safe space.
- pjc50 1y agoIf you're doing cutting edge work, then by definition you're in an area you don't fully understand.
- _heimdall 1y agoThis hasn't been my experience. When I'm unfamiliar with a codebase, or unfamiliar with a particular corner of the code, I find myself reaching for console debugging. Its a bit of a scattershot approach, I don't know what I'm looking for so I console log variables in the vicinity. Once I know a codebase I want to debug line by line, walking through to see where the execution deviates from what I expected. I very frequently lean on conditional breakpoints - I know I can skip breaks until a certain condition is met, at which point I need to see exactly what goes wrong.
- tcoff91 1y agoAdding print statements sucks when you are working on native apps and you have to wait for the compiler and linker every time you add one. Debuggers hands down if you are working on something like C++ or Rust. You can add tracepoints in your debugger if you want to do print debugging in native code. In scripting languages print debugging makes sense especially when debugging a distributed system. Also logging works better than breaking when debugging multithreading issues imo. I use both methods.
- mort96 1y agoHow long are you realistically "waiting for the compiler and linker"? 3 seconds? You're not recompiling the whole project after all, just one source file typically If I wanna use a debugger though, now that means a full recompile to build the project without optimizations, which probably takes many minutes. And then I'll have to hope that I can reproduce the issue without optimizations.
- MaulingMonkey 1y ago> How long are you realistically "waiting for the compiler and linker"? 3 seconds? I've worked on projects where incremental linking after touching the wrong .cpp file will take several minutes... and this is after I've optimized link times from e.g. switching from BFD to Gold, whereas before it might take 30 minutes. A full build (from e.g. touching the wrong header) is measured in hours. And that's for a single configuration x platform combination, and I'm often the sort to work on the multi-platform abstractions, meaning I have it even worse than that. > If I wanna use a debugger though, now that means a full recompile to build the project without optimizations You can use debuggers on optimized builds, and in fact I use debuggers on optimized builds more frequently than I do unoptimized builds. Granted, to make sense of some of the undefined behavior heisenbugs you'll have to understand disassembly, and dig into the lower level details when the high level ones are unavailable or confused by optimization, but those are learnable skills. It's also possible to turn off optimizations on a per-module basis with pragmas, although then you're back into incremental build territory.
- 1y ago
- nc0 1y agoYou also have to remember the context. First that visual debugging was still small and niche, probably not suited to the environment at Bell Labs at the time, given they were working with simpler hardware that might not provide an acceptable graphical environment (which can be seen as a lot of the UNIX system is oriented around the manipulation of lines of text). This is different from the workplace where most game developers, including J. Carmack were, with access to powerful graphical workstations and development tools. Secondly there’s also a difference on the kind of work achieved: the work on UNIX systems mostly was about writing tools than big systems, favoring composition of these utilities. And indeed, I often find people working on batch tools not using visual debuggers since the integration of tools pretty much is a problem of data structure visualization (the flow being pretty linear), which is still cumbersome to do in graphical debuggers. The trend often is inverted when working on interactive systems where the main problem actually is understanding the control flow than visualizing data structures: I see a lot more of debuggers used. Also to keep in mind that a lot of engineers today work on Linux boxes, which has yet to have acceptable graphical debuggers compared to what is offered in Visual Studio or XCode.
- bregma 1y agoWhy the emphasis on the use of cartoons (graphical debuggers) for analyzing problems with the text of computer code?
- bogomog 1y agoI think graphical debuggers are a big help: 1. It separates the meta-information of the debugger into the graphical domain. 2. It's easier to browse code and set/clear breakpoints using the mouse than the keyboard.
- eternityforest 1y agoI prefer a debugger first workflow, I'm ideally always running in the debugger except in production, so I'm always ready to inspect a bug that depends on some obscure state corruption.
- osigurdson 1y agoIt seems unlikely that John Carmack doesn't understand his problem domain. Rather it is more likely the problem domain itself, i.e., game dev vs web dev. Game dev is highly stateful and runs in a single process. This class of program can logically be extended to any complex single computer program (or perhaps even a tightly coupled multi-computer program using MPI / related). Web dev effectively runs on a cluster of machines and tends to offload state to 3rd parties (like databases that, on their own look more like game dev) and I/O is loosely coupled / event driven. There is no debugger that can pause all services in web dev such that one can inspect the overall state of the system (and you probably don't want that). So, logging is the best approach to understand what is going on. In any case, my suggestion is to understand both approaches and boldly use them in the right circumstance. If the need arises, be a rebel and break out the debugger or be a rebel and add some printfs - just don't weakly follow some tribal ritual.
- johnfn 1y agoI posted this elsewhere in the thread, but if you listen to Carmack in the interview, it's quite interesting. He would occasionally use one to step through an entire frame of gameplay to get an idea of performance and see if there were any redundancies. This is what I mean by "doesn't understand the problem domain". He's a smart guy, but no one could immediately understand all the code added in by everyone else on the team and how it all interacts.
- chickenzzzzu 1y agoThankfully, we live in an era where entire AAA games can be written almost completely from scratch by one person. Not sarcasm. If I wrote the code myself, I know where almost everything is that could go wrong. It should come as no surprise that I do not use a debugger.
- otikik 1y agoYeah I coded a AAA game yesterday.
- 1y ago
- momocowcow 1y agoThis statement explain his position very clearly. Anyone who did any serious DOS programming understands it well. "A debugger is how you get a view into a system that's too complicated to understand. I mean, anybody that thinks just read the code and think about it, that's an insane statement, you can't even read all the code on a big system. You have to do experiments on the system. And doing that by adding log statements, recompiling and rerunning it, is an incredibly inefficient way of doing it. I mean, yes, you can always get things done, even if you're working with stone knives and bare skins."
- kossae 1y agoI still don't understand how, with a properly configured debugger, manually typing print statements is better than clicking a breakpoint at the spot you were going to print. Context overload might be an issue, but just add a 'watch' to the things you care about and focus there.
- zeta0134 1y agoTwo situations immediately come to mind, though the second is admittedly domain specific: 1. If I actually pause execution, the thing I'm trying to debug will time out some network service, at which point trying to step forward is only going to hit sad paths 2. The device I'm debugging doesn't *have* a real debugger. (Common on embedded, really common for video games. Ever triggered a breakpoint in a graphics shader?) Here I might substitute "print" for "display anything at all" but it's the same idea really.
- tretiy3 1y agowhile inspecting some code inside loop, i prefer to put print and see all iterations at once in my screen, instead of countless clicking "continue" in debugger.
- commandlinefan 1y ago> Print debugging is most useful when you understand the code quite well Every debugger I've ever worked with has logpoints along with breakpoints that allow you to _dynamically_ insert "print statements" into running code without having to recompile or pollute the code with a line of code you're going to have to remember to remove. So I still think debuggers win.
- const_cast 1y agoThe thing about print statement debugging is that it's trivial to replicate in a debugger - just put break points in those few areas where you're curious. It's a tiny bit faster than writing the print statements.