9 ms·
There was a good discussion on this topic years ago [0]. The top comment shares this quote from Brian Kernighan and Rob Pike, neither of whom I'd call a young g
by demosthanos 1y ago
There was a good discussion on this topic years ago [0]. The top comment shares this quote from Brian Kernighan and Rob Pike, neither of whom I'd call a young grug:
> 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 tend to agree with them on this. For almost all of the work that I do, this hypothesis-logs-exec loop gets me to the answer substantially faster. I'm not "trying to run the code forwards in my head". I already have a working model for the way that the code runs, I know what output I expect to see if the program is behaving according to that model, and I can usually quickly intuit what is actually happening based on the incorrect output from the prints.
[0] The unreasonable effectiveness of print debugging (349 points, 354 comments) April 2021 https://news.ycombinator.com/item?id=26925570 https://news.ycombinator.com/item?id=26925570
- samsepi01 1y agoBut can't you instead just set a breakpoint next to wherever you are gonna put that print stmt and inspect value once code hits? print stmt seems like extra overhead
- teaearlgraycold 1y agoThat doesn’t necessarily give you a clean log to review
- piva00 1y agoWhile also avoiding having to re-run cases to get new introspection when you forgot to add a print statement. I tend to do both, print statements when I don't feel I want to be stepping through some cumbersome interplay of calls but diving into the debugger to step through the nitty gritty, even better when I can evaluate code in the debugger to understand the state of the data at precise points. I don't think there's a better or worse version of doing it, you use the tool that is best for what introspection you need.
- zb 1y agoDebuggers allow you inspect stuff forward in time, while print statements allow you to debug backwards. (There was a lot of academic work on reversible debuggers at one point; to be honest I haven’t kept up on how that turned out.) If you can detect a problematic condition and you want to know what will happen next, a debugger is a great tool. If you can detect a problematic condition and you need to find out what caused it, it’s printf all the way. My theory is that different types of programming encounter these two types of problems at different relative rates, and that this explains why many people strongly prefer one over the other but don’t agree on which.
- james_marks 1y agoAdding these print statements is one of my favorite LLM use cases. Hard to get wrong, tedious to type and a huge speed increase to visually scan the output.
- Freedom2 1y agoAgreed. Typically my debugger use case is when I'm exploring a potentially unknown range of values at a specific point in time, where I also might not know how to log it out. Having the LLM manage all of that for me and get it 95% correct is the real minor superpower.
- titanomachy 1y agoI do a lot of print statements as well. I think the greatest value of debuggers comes when I’m working on a codebase where I don’t already have a strong mental model, because it lets me read the code as a living artifact with states and stack traces. Like Rob Pike, I also find single-stepping tedious.
- jacques_chester 1y ago> Brian Kernighan and Rob Pike Most of us aren't Brian Kernighan or Rob Pike. I am very happy for people who are, but I am firmly at a grug level.
- legends2k 1y agoThis! Also my guess would be Kernighan or Pike aren't (weren't?) deployed into some random codebase every now and then, while most grugs are. When you build something from scratch then you can get by without debuggers, sure, but foreign codebase, a stupid grug like I can do much better with tools.
- recursivedoubts 1y agoI think a lot of “naturals” find visual debuggers pointless, but for people who don’t naturally intuit how a computer works it can be invaluable in building that intuition. I insist that my students learn a visual debugger in my classes for this reason: what the "stack" really is, how a loop really executes, etc. It doesn't replace thinking & print debugging, but it complements them both when done properly.
- sethjgore 1y agoWhat do you mean “visual debugger?”
- XorNot 1y agoPresumably an IDE rather then dealing the gdb CLI.
- travisgriggs 1y agoI think it depends on the debugger and the language semantics as well. Debugging in Swift/Kotlin, so so. The Smalltalk debugger was one of the best learning tools I ever used. “Our killer app is our debugger” doesn’t win your language mad props though.
- knome 1y agoI don't often need gdb, but I appreciate the emacs mode that wraps it every time.
- porridgeraisin 1y agoIn vscode when you step to the next statement it highlights in the left pane the variables that change. Something like that. It's useful for a beginner e.g in a for loop to see how `i` changes at the end of the loop. And similarly with return values of functions and so on.
- recursivedoubts 1y agothe intellij debugger, for example, as opposed to a command line debugger
- throwaway173738 1y agoI tend not to use a debugger for breakpoints but I use it a lot for watchpoints because I can adjust my print statements without restarting the program
- kapildev 1y agoExactly, these judiciously placed print statements help me locate the site of the error much faster than using a debugger. Then, I could switch to using a debugger once I narrow things down if I am still unsure about the cause of the problem.
- josephg 1y agoThere's another story I heard once from Rob Pike about debugging. (And this was many years ago - I hope I get the details right). He said that him and Brian K would pair while debugging. As Rob Pike told it, he would often drive the computer, putting in print statements, rerunning the program and so on. Brian Kernighan would stand behind him and quietly just think about the bug and the output the program was generating. Apparently Brian K would often just - after being silent for awhile - say "oh, I think the bug is in this function, on this line" and sure enough, there it was. Apparently it happened so often enough that he thought Brian might have figured out more bugs than Rob did, even without his hands touching the keyboard. Personally I love a good debugger. But I still think about that from time to time. There's a good chance I should step away from the computer more often and just contemplate it.
- artursapek 1y agosome of my best work as a programmer is done walking my dog or sitting in the forest
- deleted 1y ago[deleted]
- rusk 1y agoIt’s amazing what even the most subtle perturbation in output can tell you about the internal state of the code.
- noisy_boy 1y agowhere you=Brian
- notnullorvoid 1y agoSounds like Rob does use a debugger and it's name is Brian.
- dgb23 1y agoYou might be confusing Brian with Ken?
- never_inline 1y agoI use single-stepping very rarely in practice when using a debugger, except when following through a "value of a variable or two". Yet it's more convenient than pprint.pprint() for that because structured display of values, eval expression, and ability to inspect callers up the stack.
- johnfn 1y agoOn 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.
- neogodless 1y ago> time to decide where to put print statements But... that's where you put breakpoints and then you don't need to "single-step" through code. Takes less time to put a breakpoint then to add (and later remove) temporary print statements. (Now if you're putting in permanent logging that makes sense, do that anyway. But that probably won't coincide with debugging print statements...)
- demosthanos 1y agoTrue, but then you're still left stepping through your breakpoints one by one. Printf debugging gives you the full picture of an entire execution at a glance, allowing you to see time as it happened. The debugger restricts you to step through time and hold the evolution of state in your memory in exchange for giving you free access to explore the state at each point. Occasionally that arbitrary access is useful, but more often than not it's the evolution of state that you're interested in, and printf gives you that for free.
- TeMPOraL 1y agoYou can use tracepoints instead of breakpoints, or (easier, at least for me), set up breakpoints to execute "print stack frame, continue" when hit - giving you the equivalent of printf debugging, but one you can add/remove without recompiling (or even at runtime) and can give you more information for less typing. And, should this help you spot the problem, you can easily add another breakpoint or convert one of the "printing" ones so it stops instead of continuing. And of course, should the problem you're debugging be throwing exceptions or crashing the app, the debugger can pause the world at that moment for you, and you get the benefit of debugging and having a "printf log" of the same execution already available.
- Gibbon1 1y agoI set a break point, look at the variables in play and then start looking up the call stack.
- Jensson 1y ago
- winrid 1y agoThere's this idea that the way you use a debugger is by stepping over line after line during execution. That's not usually the case. Setting conditional breakpoints, especially for things like break on all exceptions, or when the video buffer has a certain pattern, etc, is usually where the value starts to come in.
- ozim 1y agoI wonder if Brian Kerninghan was using modern tooling or that comment was using quote from 70’s.
- demosthanos 1y agoIt's from The Practice of Programming, published 1999. Not a lot has changed in debuggers since then from what I can see.
- ozim 1y agoDebugging in Visual Studio by Microsoft has changed a lot in last 5 years, JetBrains IDE debugging a lot as well. I can debug .NET application and change code live, change variable states if needed. Watch variables and all kinds of helpers like stack navigation was immensely improved since I started 15 years ago. I can say debugging Java/.NET applications is totally different experience than using debugger from 1999. Multi threaded apps debugging and all kinds of helpers i visual debuggers. I just fail to see why someone would waste time putting in debug statements when they can configure debug session with conditional break points.
- BlackFly 1y agoThe tools are not mutually exclusive. I also do quite a lot with print debugging, but some of the most pernicious problems often require a debugger. > It takes less time to decide where to put print statements than to single-step to the critical section of code Why would you ever be single-stepping? Put a break point (conditional if necessary) where you would put the print statement. The difference between a single break point and a print statement is that the break point will allow you to inspect the local variables associated with all calls in the stack trace and evaluate further expressions. So when do you debug instead of using print statements? When you know that no matter what the outcome of your hypothesis is, that you will need to iteratively inspect details from other points up the stack. That is, when you know, from experience, that you are going to need further print statements but you don't know where they will be.
- edanm 1y agoThis depends on a lot of things. For example, one thing you wrote that jumps out at me: > I already have a working model for the way that the code runs [...] This is not always true. It's only true for code that I wrote or know very well. E.g. as a consultant, I often work on codebases that are new to me, and I do tend to use debuggers there more often than I use print debugging. Although lots of other variables affect this - how much complicated state there is to get things running, how fast the "system" starts up, what language it's written in and if there are alternatives (in some situations I'll use a Jupyter Notebook for exploring the code, Clojure has its own repl-based way of doing things, etc).
- TZubiri 1y agohttps://i.imgflip.com/9xrblc.jpg https://i.imgflip.com/9xrblc.jpg
- hajhatten 1y agoThere's a time and place for everything, and an escalation of tooling/environmental context for me. 1. Printing vars in unit tests may be the fastest first approach. If i know where the bug may be. 2. When that fails i usually bring in debuggers to unit tests. 3. When these aren't helping, you need debuggers on the entire binary. 4. Still stuck? Use a debugger in production.
- TZubiri 1y agoIsn't 4 very unsafe? I wouldn't trust my code to pause in places that it doesn't usually pause in. 3. What if the binary interacts with other networked computers, you gonna debug all of them? Do you end up instrumeting the whole internet? You scope out, you spiral out of control until someone puts a limit.
- nradov 1y agoYou probably just don't know how to use conditional breakpoints effectively. This is faster than adding print statements.
- splitframe 1y agoI feel like you need to know when to use what. Debugger is so much faster and easier for me when looking for errors in the use of (my own) abstractions. But when looking for errors in the bowels of abstracted or very small partitioned code print logs are far easier to see the devil in the detail.
- jemmyw 1y agoI personally use both, and I'm not sure I find the argument about needing to step through convincing. I put the debugger breakpoint at the same place I might put a print. I hardly ever step through, but I do often continue to reach this line again. The real advantage is that you can inspect the current state live and make calls with the data. However, I use prints a lot more because you can, as you say, usually get to the answer faster.
- nmeofthestate 1y agoI disagree - using an interactive debugger can give insights that just looking at the code can't (tbf it might be different for different people). But the number of times I have found pathological behaviour from just stepping through the code is many. Think "holy f**, this bit of code is running 100 times??" type stuff. With complex event-driven code written by many teams, it's not obvious what is happening at runtime by just perusing the code and stroking one's long wizard beard.
- latexr 1y ago> I disagree - using an interactive debugger can give insights that just looking at the code can't This in no way disagrees with the quote. Both can be true. The quote isn’t saying debuggers can’t provide unique insights, just that for the majority of debugging the print statement is a faster way to get what you need.
- Xeoncross 1y agoThat is the difference between complex state and simple state. I use a debugger when I've constructed a complex process that has a large cardinally of states it could end up in. There is no possibility that I can write logic checks (tests) for all source inputs to that state. I don't use one when I could simply increase test situations to find my logical error. Consider the difference between a game engine and a simple state machine. The former can be complex enough to replicate many features of the real world while a simple state machine/lexer probably just needs more tests of each individual state to spot the issue.
- owlstuffing 1y agoTheir comment conflates debugging with logging. Professional debuggers such as the one in IntelliJ IDEA are invaluable regardless of one's familiarity with a given codebase, to say otherwise is utter ignorance. Outside of logging, unless attaching a debugger is impractical, using print statements is at best wasting time.
- norir 1y agoPerhaps consider that your experience is not universal and that others have good reasons for their decisions that are not simply ignorance.
- owlstuffing 1y agoI didn't say there aren't acceptable reasons to reach for the print statement, there are. But for the vast majority of codebases out there, if good debugger tooling is available, it's a crime not to use it as a primary diagnostics tool. Claiming otherwise is indeed ignorant if not irresponsible.
- noisy_boy 1y agoDebugging is for the developer; logging is for all, and especially those who need to support the code without the skills/setup/bandwidth to drop into a debugger. Once you start paying attention to what/where/how of logging, you (and others) can spot things faster then you can step through the debugger. Plus logs provide history and and searchable.
- danfunk 1y agoThis feels a little like "I don't use a cordless drill because my screw driver works so well and is faster in most cases" grug brain says use best tool, not just tool grug used last.
- gorjusborg 1y ago> substantially faster Than what? In languages with good debugger support (see JVM/Java) it can be far quicker to click a single line to set a breakpoint, hit Debug, the inspect the values or evaluate expressions to get the runtime context you cant get from purely reading code. Print statements require rebuilding code and backing them out, so its hard to imagine that technique being faster. I do use print debugging for languages with poor IDE/debugger support, but it is one big thing I miss when outside of Java.
- eikenberry 1y agoOne thing this quote doesn't touch is that speed of fixing the bug isn't the only variable. The learning along the way is at least as important, if not more so. Reading and understanding the code serves the developer better long term if they are regularly working on it. On the other hand debuggers really shine when jumping into a project to help out and don't have or need a good understanding of the code base.