18 ms·
The unreasonable effectiveness of print debugging
- chromanoid 6y agoI usually only do print debugging when I encounter a Heisenbug. I mainly develop in Java, maybe my choice is related to its really really great debugging tooling.
- ben509 6y agoThe worst is when you put print statements in and a bug goes away, and you realize there's some kind of instruction reordering bullshit at work.
- chromanoid 6y agoA simple toString() with side effects can be insidious when mixed with other bugs...
- misiti3780 6y agoI have been developing large-scale django apps on ec2 for a while and the solution that has been working best for me is a lot of logger.** statements sent to papertrail.
- mikeech 6y agoDon’t let debugger heavy users tell you off
- enriquto 6y agoWhy "unreasonable"? There's nothing unreasonable nor wrong about print debugging. Moreover, it's a great first step towards logging and testing.
- xnx 6y ago"Unreasonable" here means that it works way better than it should for something so simple. It's a somewhat common phrasing. Example: "The Unreasonable Effectiveness of Data" https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/35179.pdf https://static.googleusercontent.com/media/research.google.c...
- falcolas 6y agoIt’s “unreasonably effective”, meaning that a cost/benefit analysis is very clearly tilted towards benefit.
- elseweather 6y agoIt's a meme, like "_____ Considered Harmful". That one started with "GOTO Considered Harmful". This one started with: https://en.wikipedia.org/wiki/The_Unreasonable_Effectiveness_of_Mathematics_in_the_Natural_Sciences https://en.wikipedia.org/wiki/The_Unreasonable_Effectiveness...
- yetihehe 6y agoBesides "behaviour in time", print debugging is effective because it's typically an extract of the most interesting for programmer things. I have a debugger window open this very moment and I can see about a hundred lines with various information about one structure, but I'm interested only in two (and I have to rerun this several times, because state got corrupted somewhere earlier).
- Dzugaru 6y agoI stopped doing step debugging at all many years ago. For me it looks the same as visual vs. text programming. Text and text search tools are just miles ahead of clicking buttons.
- lights0123 6y agoThere's always gdb/lldb from the command line.
- hobs 6y agoSo (for instance) in PowerShell my code breaks right as it is about to fail, with the state intact (ErrorActionPreference Break) which allows me to effectively fix whatever problem is about to occur and immediately have the state at the time of failure. I dont understand how printing text could EVER approach this given I can test my assumptions right away and generally only need 1 error to happen to understand the totality of the circumstances.
- jollybean 6y agoQt Creator debugger fails on me constantly, it's 2021 and the leading C++ plaf. is completely unreliable in that many more cases. That's why 'I must' use print debugging, because the 'powers that be' still provide a broken, half-baked solution 30 years in. Print debugging is however so powerful, I think there almost should be a mechanism built into languages and tooling around it so that it becomes part of the process instead of a 'kind of workaround'. It's something we all do, constantly, and yet you'll never hear about it when people are arguing about Rust or Go.
- ddingus 6y agoThis kind of thing actually got added to a Microcontroller and its native SPIN language. There is "SEND" which can be used as an ad hoc comms channel, aimed at a program method. And a debug system that can both stream data, text and graphics to a client, as well as capture and report on the state of the 8 CPU cores possibly running. https://www.parallax.com/propeller-2-graphical-debug-tools-in-spin2/ https://www.parallax.com/propeller-2-graphical-debug-tools-i... The debug output is something like using an xterm with Tektronix emulation turned on, and with all the lower level bits packaged away. The use can do a lot, from a "print" type operation to sophisticated graphics, static or animated. On the capture side, a sort of supervisor region of RAM is reserved to for an ISR to capture processor state, or anything in memory really. Can be time, or event driven.
- winrid 6y agoHave you tried Clion?
- jollybean 6y agoWell I just tried it and thanks for reminding me it was an option. I like it so far, we'll see how the debugger works, but IDE's cannot rid us of the cobwebs of arcane languages. C/C++ I think are the worst, the number of pitfalls and amount of needless complexity is byzantine and an enormous strain on mental energy.
- hpoe 6y agoSo I'd be curious. I usually work in scripted languages Bash, Ruby, JS (bleh) a bit of python. Sometimes I do some Java work though and I usually end up going to print debugging because trying to figure out all the Java logging framework, or not ending up like 40 layers deep in some magic framework dependency that is interecepting my code which is what always happens when I use a debugger. That being said do those who work in compiled languages make more heavy use of debuggers?
- seneca 6y ago> do those who work in compiled languages make more heavy use of debuggers? I work in both quite a bit. I actually think I end up using a debugger more in e.g. Python because I'm more often asking questions like "what is the type of the thing being passed here", which is not a thing I need to seek out in something like Go. That said, I think it's more a style difference than anything. I use debuggers in both compiled and noncompiled languages when I need a deeper look, and I'd guess people who don't use debuggers in scripting languages wouldn't use them in compiled languages. Probably also has to do with the ecosystem and how easy/effective debuggers are.
- UglyToad 6y agoI wonder if C# is a bit of an outlier here. I work mainly in C# but also sometimes do Type/JavaScript. While I've got the VS Code debugger running for JavaScript projects I'll rarely use it and generally use print debugging. In C# I'd almost never use print debugging and the whole thing seems ridiculously antiquated (it's partly why I hate JS work). [Assuming you're working in Visual Studio...] You literally hit 1 key, F5 and then you can step through, time travel, edit and continue. I wonder if people just haven't experienced the ease of debugging in .NET with VS. I'd say I write probably 50% of my code in a debugging session, edit and continue is a game changer. I did a little Java work in Intellij and it was similar but I think partly due to a lack of familiarity with the UI didn't feel quite as powerful.
- goalieca 6y agoPrint debugging is the only way in a distributed system the way we are building micro services these days. We just call it logging. Edit: ..and do it in production
- marco_craveiro 6y agoCompletely agree.
- kstrauser 6y agoAmen. “What do you use for debugging prod services?” “CloudWatch.”
- yaantc 6y agoYes. The same apply to a lot of embedded systems. When you can't stop the system you better learn how to debug from logs. And put the right logging support in place ahead of time: it may be impossible to replace the software in place by a new debug version, and then it's only based on preexisting logs and just adapting the logs configuration.
- roca 6y agoIt doesn't have to be that way, record-and-replay debugging can overcome this.
- razorfen 6y agoITT: a non-controversial opinion shared by most programmers. Print debugging is fast in many cases and requires little mental overhead to get going. But for some/many systems, there's a huge startup and cooldown time for their applications - and compiling in a print, deploying the service, and then running through the steps necessary to recreate a bug is a non-trivial exercise. Think remote debugging of a deployed system with a bug that requires select network and data states that are hard or impossible to replicate in local/dev. For things like this, being able to isolate the exact point of breakage by stepping through deployed code, and doing immediate evaluation at various points to interrogate state can't be beat. This post strikes me as either (a) a younger programmer who still thinks that tool choice is a war rather than different tools for different jobs (b) someone making a limp effort at stoking controversy for attention.
- tyingq 6y agoProbably explains why java has such a rich set of logging and debugging tools. Startup time, plus the idea that printing to stderr/stdout doesn't help you figure out where that goes in many java environments :)
- diminish 6y agoIn languages where you build a deeply nested call stack, advanced debugging looks more promissing. But in simpler setups like ASP/PHP/JSP etc, simply printing works fine.
- jollybean 6y agoOr c) someone just making comments from observed experience, and there's not much about that 'senior developers' have when it comes to 'having had to compile something that takes a while' - that's the purview of everyone, or at least, those who have worked on those larger projects. And though remotely debugging code definitely happens, it's in relative terms, very rare. This is just someone making a comment on their blog, that's it.
- DangitBobby 6y ago> I should emphatically mention: I’m not saying that print debugging is the best end state for debugging tools, far from it. I’m just saying that we should reflect deeply on why print debugging is so popular, beyond mere convenience, and incorporate those lessons into new tools. I'm not sure what about the article makes you think either a or b. They are trying to critically examine why some people reach for print debugging first, and I think it's spot on.
- hprotagonist 6y agoFor python: i specifically recommend https://github.com/zestyping/q https://github.com/zestyping/q a lot, which is like print debugging on steroids: All output goes to /tmp/q (or on Windows, to $HOME/tmp/q). You can watch the output with this shell command while your program is running: tail -f /tmp/q
- mekoka 6y agoIDE vs Text editor. OOP vs Functional. Logger vs debugger. The holy wars that shouldn't be. Why can't we all be friends and accept that Vim is better than emacs.
- catillac 6y agoI heard the latter sentiment earlier today, but I don’t think anyone is actually passionate about what editor others use. Opinionated sometimes.
- tyingq 6y agoMost of the time, I suppose. Watching someone try to write java in vim (or generally, without an IDE) gives me anxiety though, even with a language server :)
- mdpye 6y agoMeh, it's fine. In general, I find that vim in more productive most of the time (now I have a language server, before, wouldn't ever consider it!) The fluid and consistent (java is only a portion of what I write at work) editing experience is mostly more valuable to me than the slightly better autocompletion. I keep intellij installed, but it only open it if I want to do a fancy mechanical refactor, like extract an interface from an existing class. Smaller niceties like creating a local variable from an expression are only a handful of keystrokes just feel like naturally describing what I want (lexically, rather than semantically, I'll admit) in vim anyway.
- themadsens 6y agoThat's perfectly doable. I routinely navigate / extend / debug / refactor a 600 KLOC Java codebase with nvim + ctags + ripgrep and will have the job done well before the language server has even completed digging through those 600 KLOC.
- h2odragon 6y agoPassion about the tools others use can be called for if you can see they're obviously struggling to meet their goals with the tools they've chosen. The hard part is, unlike a screwdriver where you can demonstrate, editors and "IT" in general are mental tools where the mindset is an invisible, nontransferable "handle" to the visible portion that everyone can see and use.
- CJefferson 6y agoPersonally, I think my biggest reason for using print debugging is.. it works. In C++ I often find the debugger doesn't find symbols on projects built with configure/Make. If I have a Java Gradle project I have no idea how to get it into a debugger. Python debuggers always seem fragile. Rust requires I install and use a "rust-gdb" script -- except on my current machine that doesn't work and I don't know why. I'm sure in each case I'm sure I could get a debugger working given enough time, but the only error I've ever had in print debugging is failing to flush output before a crash, and it's never been hard to search for "how to flush output" in whatever language I'm current using.
- le-mark 6y agoI’m sorry but this is just ignorance, learning to use the debugger for the platform at hand is a basic skill every developer should master. So many times have seen developers use the debugger to troubleshoot and fixed issues and been perceived as a “ninja” (despise that term but that was the effect) because they knew how to use the debugger. I mean yeah keep printing lines, and keep being out performed by your debugger using peers. That’s the choice. Yes I am dieing on this hill.
- axaxs 6y agoOK. And I've also cringed watching ninjas step through code slowly, reading everything, spending 20 mins catching something 2 print statements would have achieved. Debuggers aren't bad. But neither is printing. Knowing when to reach for them is probably a bit more key.
- pjmlp 6y agoThey weren't ninjas, otherwise they would have used breakpoint actions for doing those print statements without modifying the source code.
- majjgepolja 6y agoWhat's breakpoint action? Is it like inserting printf before breakpoint?
- GuB-42 6y agoprintf debugging always have a place, but for some reason, I found the debugging experience to be worse than 20 years ago. Tools like Visual Studio still have great debuggers, but I didn't notice significant improvement since the early days, and newer toolchains are worse. A couple of years ago, I had to maintain a bit of Java code using Eclipse. That is, the old IDE everyone loves to hate. And while some of that hate is well deserved, for debugging, it was the most pleasant experience I had in a long time. Nice object inspector, edit-and-continue, conditional breakpoints, and step-by-step that works. Much better than fumbling around with GDB or one of its less-than-perfect UIs. Also note that printf debugging and the step-by-step and breakpoint kind are not mutually exclusive. With an edit-and-continue feature, you can get the best of both worlds, but that's not something common these days, unfortunately.
- vvanders 6y agoMaybe it was because I was exposed to it early in my career but I have yet to find anything that rivals Visual Studio debugging, either from a "just works" perspective or ability to deep-dive into gnarly memory corruption(memory windows, robust watch windows and data breakpoints).
- Rapzid 6y agoA lot of debuggers will also print/log and can even inject those statements into a running app where hot reloading manual print statements would otherwise not work. From there there are situations where a debugger will save a LOT of time. I'm thinking of trying to figure out what's causing a behavior in a large dependency injected application with plugins when you have little to no familiarity with all the code involved. And then of course all the other things a debugger can do for you. > Clearly Real Debuggers offer a superior experience to print debugging in so many ways. But print debugging is just easier to get started with, and it reliably works anywhere, so that’s why we use print debugging so much. I think the tone of the first sentence and the word "superior" unnecessarily creates a strawman.
- angst_ridden 6y agoThe beauty of printf debugging for a novice C programmer is that the recompiling with printfs changes the memory layout so your buffer overflow no longer segfaults you. ALternatively, your printf can use the wrong formatter string, and cause unrelated crashes. Such joy! Makes me nostalgic for the good old days.
- majjgepolja 6y ago> ALternatively, your printf can use the wrong formatter string, and cause unrelated crashes. Such joy! What compiler are you using? Aztec C? Prehistoric C?
- mjw1007 6y agoThere are two separate questions: whether you want to see some kind of trace of the program or you want to step around in its state, and whether to use a "real" debugger or not. In most cases I prefer to do something trace-based, and in the IDEs I've used the debuggers have much weaker support for that than they do for stepping around. In particular, setting up tracepoints tends to involve fiddly dialog boxes which are much less convenient than using the main text-editor interface to say what you want. I think there's plenty of scope for debuggers to provide a better interface for trace-style debugging. For example I'd like to be able to toggle a tracepoint after capturing the run, and have the lines it created appear or disappear, or add a filter expression or additional information to display without having to rerun the program.
- boatsie 6y agoA few more reasons why print debugging is used. If you are debugging multiple things at once, you’ll have breakpoints set that aren’t necessarily needed at the moment, meaning you have to continue a bunch of times to get to the right spot. Or your breakpoint needs to be in a loop that is called multiple times and conditional breakpoints are a pain and subject to code errors in the condition itself. Many debuggers are not great at examining state of objects, for instance a deeply nested object for which you want array index 42 within a dictionary of an object. Or you need to see a value that is calculated rather than just present in the current state.
- 1337shadow 6y ago> you’ll have breakpoints set that aren’t necessarily needed at the moment, meaning you have to continue a bunch of times to get to the right spot. Python: if something: breakpoint() Js: if (something) debugger; Much easier than breakpoint conditions in visual debuggers imho.
- boatsie 6y agoThat runs the risk of forgetting to remove it.
- 1337shadow 6y agoIf you don't read your commits before pushing or even merging them ... But I use `git add -p` and `git checkout -p` which works well against this
- jhgb 6y agoI had a crazy idea the other day that perhaps there could be something like "CSS for program execution traces". If you think of function identifiers as XML/HTML tags and arguments for individual function activations as element attributes, then perhaps something similar to CSS selectors but acting on the tree representation of a program's execution could trigger at certain clearly defined points during the execution and format some human-readable output of what the program was actually doing, or a "cross-section" of it at least.
- eddieh 6y agoSounds a lot like syntactic sugar or a DSL for symbolic breakpoints combined with conditionals. That's certainly doable. Something like: func1(4) > func2(null) debug; Semantically: upon func1 called with arg 4 and some descending path that calls func2 with arg null, enter the debugger Neat idea!
- jhgb 6y agoI got the idea when I was thinking about the applicability of computer algebra systems to math education. Some way of visualizing the decisions and steps of a logically complicated program seemed necessary for that. Getting a readable trace of the computation in a similar way to the one that some logical programs or expert systems can justify their reasoning with seemed like a usable form of such a visualization, and some time later then the analogy with CSS/XSLT struck me. I was thinking of collecting all the steps into an output, but setting breakpoints in a similar fashion with individual "selectors" could be useful for debugging, too.
- pestatije 6y agoFor production, the only way is logs (print debugging).
- gdubs 6y agoGPU equivalent: putting stuff into vertex buffers so you can inspect the buffer and see if the expected values are there.
- rubyist5eva 6y agoPrint debugging is great but you'll pry the IntelliJ debugger from my cold dead hands.
- mistahenry 6y agoCompletely agree. When implementing new functionality in my Kotlin Spring Boot apps, I find the debugger crucial for fixing any exception that isn’t immediately clear. I’ll simply rerun my test with a breakpoint on the failing line, peruse the values of local variables (often spelunking deep into nested objects), and test theories with the window that lets my evaluate arbitrary expressions. Occasionally, I’ll change the value of a local variable and let the program continue to see if that value would fix the issue. It’s a workflow that makes “Wait, why did that happen” such an easy question to answer.
- njharman 6y agoI think it has most to do with way user thinks. I need to see big picture, whole state, all the stuff and rapidly jump back and forth. I also, supposidely, have ability to keep a lot of state / scope/ abstraction in my head. So I find print debugging sufficient and fast. Rarely encounter situation I feel need for "stronger" tool. Where other people focus on one thing, all that simultaneous output is just noise and distraction to them. And based on the continued use and popularity of step-based debuggers, these people are much more productive (and happier) using those type of tools. It's very important to understand neither system is inherently superior. Although one or the other is superior to each individual. [btw over 35yrs of tech industry / software development I've found this true, that tools/paradigms are not universally superior but are superior based on individual) for many subjects. All the ones that have internal debates in techdom]
- praptak 6y agoYou call it print debugging, I call it a powerful experimental framework for testing hypotheses about the behaviour of the program.
- breck 6y agoI hate coding in an environment that does not easily support step-wise debugging. And yet, I use printf 10x-100x more frequently. Printf is actually causing you to do some thinking, and writing a little bit of code to conduct an experiment that hopefully will tell you in one shot what the problem is on a simple run. Step-wise debugging instead forces you to think about the problem, but then go through carefully and run a lot of mental load at each "next step" push to figure it out. That being said, there's almost no good reason for a platform to not support step-wise debugging, so it's a big code smell that you're going to have a bad time in general there (even if in practice you'd largely use printf anyway).
- varispeed 6y agoThere are environments where printf is not possible - e.g. MCU development. For instance, if the code breaks before the serial port is setup for printf to work.
- mark-r 6y agoYou can always just send the output to a block of memory that is reserved for debugging, and dump out that block when necessary.
- marco_craveiro 6y agoI disagree slightly with the emphasis on "print debugging". I think what is missing is a body of theory around logging as a methodology. When I write code, I like to be able to look at the log file and "see" what the code is doing, when on DEBUG or higher. I think logging is a difficult but very important skill, and one which we are losing over time. If anyone is aware of any good books on logging (even if very old), do let me know. Seems like "logging theory" is a missing subject in Software Engineering. I also don't see any contradiction between liking good logs and using the debugger when needed.
- strikhedonia_ 6y agoAbsolutely. When people say they use 'print statements,' are they talking about log points (a debugger construct), logging or something else? I should hope that, in most cases, they're not literally modifying their source code to achieve this. While there are a handful of scenarios in which this is necessary, on the whole it strikes me as inefficient, time-consuming and error-prone. In most environments, there are better ways to make this data observable.
- diegocg 6y agoI have never spent much time learning debuggers honestly. I'm not sure if what I want exists: I would love to have a debugger that offers a partial text editor experience, eg. it shows my code, I move the cursor to some statement, then I press some key binding and the debugger starts printing (in another window) all the state changes in that statement. Another key binding prints all the state changes in the entire function, etc. All of this while the program is running. Are there debuggers that can do this? I have used gdb in the past, but having to set up breakpoints by hand and remembering names makes it too tedious.
- Agingcoder 6y agoYes, it's called pernosco, and it's quite remarkable. However, it works from a recording of your program. https://www.pernos.co https://www.pernos.co
- varispeed 6y agoThe best way of finding faults for me is writing a test that fails the problematic condition and then use prints in all parts that I think are being executed and may have key information to help solving the mystery. I tried using debuggers, but it was always too much hassle.
- Jeff_Brown 6y agoFor print debugging in Python I recently discovered a nice little time-saver: the icecream package. Rather than having to type "print( "x: ", x )", you can instead type type "ic(x)". [1] https://github.com/gruns/icecream https://github.com/gruns/icecream
- kstrauser 6y agoAlso, “print(f’{x=}’)” without an external dependency.
- Jeff_Brown 6y agoCool! Where can I read about what's going on in that?
- kstrauser 6y agoGo to https://docs.python.org/3/reference/lexical_analysis.html#f-strings https://docs.python.org/3/reference/lexical_analysis.html#f-... and search for “ New in version 3.8: The equal sign '='.”
- deleted 6y ago[deleted]
- pbiggar 6y agoBoth suck. With a debugger, you need to set up a debugger and step through (and often, they don't work quite as well as you hope). With print debugging, you need to add the print statements. In both, you can't retroactively debug already executed code. This is one of the areas where I'm really proud of what we did in Dark. In Dark (https://darklang.com https://darklang.com), all execution is traced and you can see the value of any expression on any trace by putting your cursor in the expression. Advantages: - no struggle to reproduce the error - no need to set up a debugger - no need to add print statements When I write Dark, I can debug in seconds. When I work on the Dark implementation (F# or ReScript), I spend at least minutes on each bug because I need to do a bunch of setup to find enough information to diagnose the error.
- tester756 6y agoI'd say that except some heavily multithreaded cases, then print approach may be due to lack of mature tooling I can't understand why would anyone prefer to write some print, when you can have Visual Studio's * break point * conditional break point * ability to place another break points when you're already on the other * expression evaluation at fly!! * decent possibility to modify code at fly I still remember case where I modified function with line with bad SQL (breakpoint after executing this SQL), added call to the same function with the same parameters after this breakpoint, let it execute again, caught the breakpoint once again and removed that call to itself and all of that without recompiling program! it felt like magic
- username90 6y agoPrint statements lets you see many things at once. Breakpoint only breaks at one thing. They are good for different things.
- tester756 6y ago> Breakpoint only breaks at one thing. One breakpoint breaks at one thing, that's why you can have many of them
- username90 6y agoBut it only stops at one thing at once. With print debugging I can print 100 things in different places and look at all of them at once giving me a temporal overview, I can't do that in a debugger.
- Someone1234 6y agoThis is why products like OzCode for Visual Studio[0] are interesting. With their ability to put in a breakpoint and see multiple variable's values instantly and "time travel" (i.e. limit step back through logic), it kind of gives you the print debugging benefits in regular debugging. I've not seen anyone else try anything like this. There's a YouTube demo here: https://youtu.be/82jq5cvl67E?t=1561 https://youtu.be/82jq5cvl67E?t=1561 [0] https://oz-code.com/ozcode-production-debugger https://oz-code.com/ozcode-production-debugger
- roenxi 6y agoProbably the most interesting thing about development as a discipline is the near radio silence on how to debug. There is a decided lack of academic success in engaging with debugging as an object that can be studied. There are channels to learn about debugging as a stand-alone topic. Programmers don't often talk about debugging techniques in my experience. For something that takes up the overwhelming bulk of a developer's time the silence is in many ways deafening. It may be that nobody has a method superior to print debugging.
- damagednoob 6y agoI mean there are other types of debuggers (step-through, time travel[1]) but I agree with you that I have never seen any research on which is better/faster. It seems an obvious topic so it makes me suspect that it comes down to individual style. [1] https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview https://docs.microsoft.com/en-us/windows-hardware/drivers/de...
- spaetzleesser 6y ago“ which is better/faster” Because the answer is “it depends”. As you mentioned it’s a lot about individual style and preference. I have seen a lot of good coders that got things done but worked in totally different ways.
- Swizec 6y agoDebugging is impossibly difficult to teach. It's much closer to "how to solve an escape room" than it is to "how to build X". Debugging requires deep understanding what you are doing and your system. It's different every time. And while there's a general algorithm you can follow: 1. Guess what's wrong 2. Ask "How would I prove that's wrong?" 3. Try it 4. If bug found, fix, if not go back to 1 How would you teach that other than asking people to go solve a bunch of real world problems in real world systems for a few years?
- slibhb 6y ago> 1. Guess what's wrong 2. Ask "How would I prove that's wrong?" 3. Try it 4. If bug found, fix, if not go back to 1 I think this is exactly right. Teaching 1 is impossible I guess but the general method (it's basically the scientific method in an ideal environment) seems teachable. Come up with a hypothesis of what's wrong, try to prove or disprove the hypothesis.
- Agingcoder 6y ago(I have already replied to another comment with the same suggestion). Pernosco offers the best of both worlds ( debugger, print), along with a few magical features. https://www.pernos.co https://www.pernos.co With it you can print anything present in your recording and step, and do anything you'd do in a regular debugging.
- mjw1007 6y agoIt's good to see that they hope to provide « the best printf debugging experience you've ever had », but I'm disappointed at the UI they show on the "Condition and print expressions" page. The video shows the user using their mouse and typing the expression to be printed into a tiny text input. Part of the attraction of print-style debugging is the convenience of being able to use your main editor UI, along with all the conveniences that provides, to write that expression. (That might be fancy completion or vi-style editing commands or keyboard macros; it will be different for different programmers.)
- Agingcoder 6y agoI suggest you tell them! They'll probably be happy to get some feedback. If I'm honest, in spite of it not being perfect, it's much better than regular printf. The other very nice feature is dataflow (click on a variable value, and it tells you where it comes from, and it handles copies seamlessly), which makes a large number of debugging tasks trivial.
- hnnameblah365 6y agoI think there's a conflation of processes and tools which leads to the false comparison. Print debugging is a process, which uses a tool called print statements. Stepping through code is a process, which uses a tool called the debugger. Print debugging excels at triaging the problem. And every language has print statements. Ubiquitous first tier support. They help you narrow down where your assumptions about the program behavior may be wrong. Once you know what area to focus on, you pull out the debugger and step thru the code.
- thenoblesunfish 6y agoGood points. Makes me think that if print debugging is primitive, the more sophisticated alternative isn‘t a step debugger, but a logging system.
- corysama 6y agoIn my experience, people who downplay debuggers don’t have the option to use effective debuggers. Debugging C++ and especially C# in Visual Studio is wonderful. Debugging Java in Eclipse can be great. Meanwhile GDB and most other language debuggers are painful and every IDE integration I’ve seen of them has been horribly unreliable. I’ve heard there’s a culture in parts of Google where kids go through uni using GDB because “Woo Linux!” then go straight into Google where everyone is “Woo Linux!” (I do like Linux, btw) so they are either still using GDB, or more likely have given up on it and reverted to printf. So, everything takes forever to figure out and that’s just “normal”. This was coming from a console gamedev who was shocked by the transition after moving to Google. Meanwhile, I’ve spent a good part of the past couple decades debugging large volumes of code that I will literally only see once ever. With a good debugger, that can be done effectively because watching and even modifying the code’s behavior can be done at a glance rather than a re-compile. I’ve also worked on a very big project that used extensive logging because they had a very bad debugger setup and productivity was in the toilet compared to every other job I’ve had. The only way I could keep productive was to take the time to break out systems into small independent programs in my own environment so that I could use a debugger on that rather the run the code where it is.
- damagednoob 6y agoI dunno. I was a C# dev for 7 years and exclusively used Visual Studio's debugger. Then went to a JRuby project which had abysmal debugger support at the time. Learned to used printf style and it's now been my goto for the last 8 years. This despite coding in Nodejs for last 4 which has pretty good support. I only reach for the step through debugger when the problem is tricky, mainly because of having to do the setup.
- thrower123 6y agoThe Visual Studio debugger is great, but there are some limitations. Anything very serious is going to be multithreaded, and if you block a thread poking in the debugger, other things are going to start timing out and the real flow of the program is interrupted and impossible to reproduce. Log heavily, and log systematically - imagine you're going to need to grep through days of logfiles to find the needle in the haystack - you will eventually. Build in runtime switches to dial log verbosity up and down. Err on the side of providing more context than less. If something throws exceptions, catch them, log exactly where it was, what it was supposed to be doing, and any relevant parameters or state. If you can get them, process dump files are unreasonably effective, too.
- Androider 6y agoSpeed of iteration beats quality of iteration. You can step through the program, reason about what's going on, tracking values as they change. But if you missed the moment, you have start again from the beginning (time traveling debuggers being rare). Or maybe you're looking at the wrong part entirely at this stage, and just wasting time. With print debugging you write a bit of code to test a hypothesis. Then you run it, and you keep running it, and especially if it's an UI program you play with the UI and see how the values change during that run. Ideally the loop to change the code -> see the result should be a few seconds. You can then git commit or stash your prints, switch branches and compare behavior with the same changes applied. And at the end of the day if you walk away, your prints will still be there the next morning. The debugger doesn't produce any comparable tangible artifacts. Once you do know where the problem is, and if it's not apparent what the problem is (most problems are pretty trivial once located), that's IMO the time to break out the debugger and slowly step through it. But the vast majority of problems are faster to solve through rapid iterative exploration with prints in my experience (C, C++ for over a decade, Python, now JS/TS).
- damagednoob 6y ago> Speed of iteration beats quality of iteration. That's especially true if you're doing some form of TDD/unit testing. With IntelliJ, I can easily set it to watch for changes and cycle one unit test while I make changes. If something weird happens I can just drop a printf in there, understand and rectify the issue, then take it out. Much faster than step through debugging.
- Shikadi 6y agoGood for simple stuff, but if you find yourself changing your print statements over and over, a breakpoint probably would have been faster
- matsemann 6y agoI often use prints to find the suspect, and then debugger to weed it out. Conditional breakpoints make it easy to stop at the correct place. About your debugging from the beginning: with Intellij on the jvm one can "drop frame", which is basically to discard the current function and start over with the stack as it was. Since I mostly write kotlin my objects are immutable, so rerunning most stuff actually works fine. And hot-swapping the function while the debugger is paused I can even try multiple implementations without having to rerun everything, just drop frame, hot swap, step into the new and updated function. I'd say knowing the debugger well and using it is a faster way to iterate than not.
- SinParadise 6y agoThe limitation of print incentivizes me to write smaller functions and code that are generally free of mutations, so traces doesn't get stale fast. Debugging on the otherhand, well.. I've just been told by my senior to write bigger functions, because the line-by-line debugging tool jumps around too much when moving between functions to functions.
- lisper 6y agoPrint debugging is useful for the same reason backtraces are useful: both allow you to see what happened in the past, which is usually where the problem you're trying to fix actually happened.
- ArtWomb 6y agoHave never found writing to logs to be "effective". More a necessary evil ;) What is really effective is "visual debugging". Say for example you are testing for bias in an RNG. Rendering a large format image of random rgb values will immediately show any cycles, even to the untrained eye. Consider GPGPU workloads, for ML or ray tracing for example. There are myriad levels of variables to track: resources, allocations, command buffer state, synchronization, compute kernel per vector, and so on. All primitives that very much lend themselves to graphical representations! Right now editing live code in a profiler usually involves textual editing of the graphical shaders. But it's easy to see how this evolves to a purely visual shader editor, not unlike those found in Unreal or Godot.
- arendtio 6y agoAnother aspect, where printf debugging can be better than debuggers are use-cases where timing is relevant. Some bugs don't occur when break points stop the program at certain points in time. For completeness is should be added, that there are also cases where the printf can change the performance and make it impossible to find a bug. I think the two methods are complementary and should be use in combination. However, the big issue is that basic printf debugging is very simple to use and debuggers have a steeper learning curve in the beginning. Therefore, people start using printf debugging and don't invest into learning how to use debuggers. And when developers don't invest into learning how to use debuggers properly, they are missing the skills to utilize them and still use printf debugging in cases when debuggers are clearly superior.
- spaetzleesser 6y ago“ I think the two methods are complementary and should be use in combination” This should be repeated many times. I am getting very tired of the constant need of people who want to have a strict ideology and find the one true way of doing things.
- maccard 6y agoIf you have a timing related issue fixed by a debugger it's probably going to be fixed by printing/logging too.
- majewsky 6y agoNot at all. Stepping through a function with a debugger usually takes on the order of seconds and minutes. Printing some stuff in the function usually takes on the order of microseconds or milliseconds. That's a difference of at least three, possibly eight or nine orders of magnitude. It's very easy to imagine a timing-related issue that's affected by a delay of X seconds, but not a delay of X microseconds (RPC calls are typically on the order of milliseconds, for instance).
- SAI_Peregrinus 6y agoDebuggers don't have to halt execution on hitting a breakpoint. They can do other things, like print the contents of memory, letting the host system handle the formatting. They're actually usually better for timing-sensitive prints than printf debugging. That said, most people don't know this is possible (the learning curve issue you mentioned), even though it's an important part of how to use debuggers!
- pmichaud 6y agoI think the point about seeing the state over time is a great one. But also I want to nitpick because the title is one of my “favorite” pet peeves: “The Unreasonable Effectiveness of ...” thing is now used (as in this article) by people who are trying to say that something is remarkably or surprisingly effective, but that’s not what the original essay was about at all! “The unreasonable effectiveness of the mathematics in the natural sciences” was a philosophy of science piece whose thesis was that there is no reasonable (rational, provable) basis for the degree to which our math abstractions and syllogisms happen to correspond to the physical universe. It is self evident that they do in fact correspond super well, but the original piece was about how weird and spooky that actually is, if you think about it at all. Math is super effective, and there is no reasonable basis that we yet know that it should be so effective. It’s unreasonably effective. It’s such a perfect title for that piece, and it feels dirty or diluting when it’s just used to mean “remarkably effective.”
- deleted 6y ago[deleted]
- jameshart 6y agoThe idea that print debugging is about being able to understand the time dimension of your code resonates, definitely. It reminded me of how the redux dev tools browser plug-in is an interesting pointer to a better kind of debugging. And essentially all that is is a rich UI around printing out the entire redux state after each operation. But because the redux state advances in discrete steps it’s very easy to express exactly what happened, and explore precisely what state change happened in response to each action. I do find myself wondering whether there’s a much richer debugging capability along those lines that could be applied more generally.
- cheeri0 6y agoYeah it works great until you install 30 frameworks and they all tell you so much useless crap that you can't see your own messages. Why do they log these useless messages? Because, they're bad programmers who are 1000 AU from being able to realize it.
- two_handfuls 6y agoI agree with the point about time travel debugging. I find it so intriguing that I've been playing with it for a little tool to make VR games. Anecdotally, it has helped me a lot with debugging.
- NoZZz 6y agoIf you've ever used visual studio to debug c/c++ code you just know why the linux crowd mentally works around it.
- adam_arthur 6y agoI've never understood print debugging, at least in a web dev/nodejs context. I don't begrudge people having their own approach to things, but almost universally when I see people use print debugging they seem to take quite a bit longer than just break pointing at the problem area. If your code is in an unexpected state, it's much easier to hit a breakpoint, examine local values, and then backstep through the call stack to see what went wrong. I dare to say that in a single threaded context, it's almost objectively more effective. Versus the alternative of using printlines, you basically need to map/model the state flow out in your head which is prone to error (limited capacity of human working memory). Is it not easier to directly see the problem rather than doing mental math to make assumptions about the problem? I can't see a case for that being more effective. Most of the time I see people print debugging it seems to be because they haven't used the debugger much... either they aren't comfortable with it, or didn't bother to set it up, or see the mental mapping approach as more "mathematical/logical"... or something. Takes you back to the school days of solving algorithms on paper :) That being said for simple problems, I've used print debugging myself (again, usually because I'm too lazy to setup the full debugger). Or for multithreaded contexts etc, where thinking it through can actually be more effective than looking directly at the problem (multiple contexts)
- nottorp 6y ago> almost universally when I see people use print debugging they seem to take quite a bit longer than just break pointing at the problem area. Could it be... because they don't know where the problem area is yet? Which is what the original article and most comments in favor of print debugging say.
- adam_arthur 6y agoHow can they not know? If you see unexpected behavior in some subsystem, it should be easy to pinpoint. e.g. in a UI context, timeline shows wrong data. Start there and work backwards. I can only imagine it's hard to pinpoint if the code is not factored well.
- billytetrud 6y agoI definitely think we should create debuggers that can step backwards. Would be incredibly helpful
- beiller 6y agoHere's a hack I do when I'm running a tight loop. In something like a video game at 60 fps, print is useless cause it spams so much in the terminal it's unreadable. So I use my hack: If math.random() > 0.99 print(debug_msg)
- utopcell 6y ago> I do want to point out that print debugging has one critical feature that most step-based debuggers don’t have: you can see program state from multiple time steps all at once. At Google, we have time-traveling debuggers neatly integrated into our cloud IDE: You can step forwards and backwards, you can inspect variables for all the values they've had or will have until program termination, and you can also see all invocations of methods (along with their parameters) that have happened or will happen. I still use logging for debugging. Cool tech aside, I think what you really need, above everything else, is the fastest possible iteration cycles.
- georgewsinger 6y agoI recently discovered a Linux debugger & tool which allowed me to solve problems 10x faster than print statements: pernos.co (which is layered over Mozilla's rr time-tracking debugger). Pernosco's tool is described pretty well on their website, but basically it allows you to view a program inside and out, forwards /and/ backwards, with zero replay lag. Everything from stack traces to variable displays (at any point in time in your code execution) is extremely easy to view and understand. The best part is the lightning fast search functionality (again: zero lag). On top of this: extraordinary customer service if anything breaks (in my experience, they fix bugs within 24 hours and are highly communicative). If you value your time I highly recommend you check out this tool.
- jauco 6y agoset -o xtrace ftw! More languages should have that.
- SPBS 6y agoI feel like the author gets close to the point but fails to drive it home: step-through debugging is unbelievably cumbersome. During a typical step-through debugging session, 90% of the time is spent on lines you are completely not interested in. Oh, did you accidentally skip the important point because of how tedious it was to keep spamming step-over/step-in? Better start over again. With print debugging, you set up your print statements strategically and -zing-, you get your results back. Feedback loop shorter. 100% of the lines are the ones you are interested in, because you put the print statements there. I'm still waiting for the feature where you can conditionally stop at some breakpoint -only- if some other breakpoint/watchpoint was crossed over. It's not a conditional breakpoint, because conditional breakpoints can only watch variables, not other breakpoints. You could of course set some variable depending on whether some section was entered and then conditionally break based on that variable. But then you're back to print debugging land, having to manually insert code in order debug the program. Debuggers are superior when it comes to interrogating the exact state of some variables, as well as the decision paths the program takes. For anything simpler, print debugging simply offers the better developer experience.
- Stratoscope 6y ago> I'm still waiting for the feature where you can conditionally stop at some breakpoint -only- if some other breakpoint/watchpoint was crossed over. PyCharm 2021.1 has this, so I would guess that other members of the IntelliJ family probably have it too. Set a breakpoint and then right-click the red dot, and click More to open the full Breakpoints dialog. Open the drop-down under "Disable until hitting the following breakpoint:" and select the other breakpoint that should enable this one. And thank you for mentioning this! I didn't know PyCharm had this feature until I took a look after seeing your comment. This will be super useful.
- kmfpl 6y agoWith lldb you can do that, basically you have the option of running commands when a given breakpoint is hit, so you can just make it place another breakpoint, and it will be placed only if the first breakpoint is hit. I assume you can do something like this on gdb as well.
- 6y ago
- 0xbadcafebee 6y agoStupid question: why don't more programming languages and/or compilers natively support the alternative to print debugging, which is (afaik) tracing? I guess some languages have it, but some don't, or they are onerous add-ons?
- deleted 6y ago[deleted]
- danbmil99 6y agoDebuggers are next to useless when dealing with today's distributed systems, all operating asynchronously in parallel. For the kind of bugs (race conditions, corner cases) that aren't easily caught by compilers, linters, unit tests or code review (in other words, the "Heisenbugs" that can stop a release in its tracks), aggressive logging is the only tool I've ever seen that is useful in-the-wild. I would put forward that proficiency with this style of debugging (closely related to useful performance profiling) is a major factor separating mediocre programmers from the quasi-mythical 10X rockstars.
- zestyping 6y agoIn one word, search. You can search the output over time.
- oweiler 6y agoModern debuggers allow you to execute log statements on breakpoints. Much better than modifying your program to output something.
- p4l4g4 6y agoI used this a lot! Combined with scripting support, you can make the experience even more interactive. Used gdb scripts in the past to make debug sessions repeatable. Stuck beyond the point your interested in? No problem, just restart the session with your gdb script and your right back on track! You can also add custom functions to output your state in a more meaningful way or to mock some state. In longer debugging sessions, a good debugger can be a life safer! Still, for shorter sessions, reading logs and adding occasional prints are hard to beat.
- majewsky 6y agoInteresting hypothesis. I think a big part of the issue is that printf debugging has always been "good enough" for me. I have used gdb in the past, but I've never felt the incentive to become good at it, so my knowledge of it atrophies and it has become a less interesting option over time. On the other hand, my knowledge of how to printf messages and extract them from the running process never atrophy because I do exactly that every day. So maybe the situation changes if ever I come across a bug that's so mindbogglingly convoluted that printf debugging is not viable. Then I'll be forced to learn to use a step debugger well, and that could change my choice of tools going forward.
- bboreham 6y ago“With enough print statements, all bugs are shallow”
- crnkofe 6y agoI find that the greater majority of the time there are better tools to solve a problem than using print statements even when considering the fact, that a project needs to be refactored to be debuggable. If I have a bug I can reproduce I can write a unit or integration test, try narrowing down the issue and use a debugger on the test itself for further help. Intellij has great support here, VS as well and there's plenty others. If the bug exists in production only using a debugger I can connect to it remotely and dump the state (thread dumps in Java or core dumps with Delve for Go). If there's an option of using a profiler it makes the experience even better especially for diagnosing performance issues. For distributed systems monitoring libraries, log aggregators are much more useful than raw logs. Proper metrics allow fast pinpointing of issues and log aggregators give me an option to either look for rare/common errors easily. The only case I'd resort to prints nowadays is as a last resort if there are no better options.
- slaymaker1907 6y agoI agree about being able to see the whole program execution. This is particularly useful for multithreaded code since it provides a linear view into how the program actually executed. How are you supposed to figure out that A happened before B in a multithreaded program using only a debugger? With adequate logging, even if you don't log the precise times for A and B, you can often infer the ordering of these events based on other logged data. For a lot of glue type code, I don't actually care about stepping through something line by line. I really want to see how components interact, not each step of execution. Though I do wish languages had better support for doing something like printing out all local variables in the current function along with the stack trace, sort of like a very shallow, low-cost dump. Another big advantage is that logging is usually much easier to turn on (or even keep on by default) for production scenarios. Good luck getting some bank to let you run a debugger or even get a dump for anything.
- saagarjha 6y ago> How are you supposed to figure out that A happened before B in a multithreaded program using only a debugger? Breakpoint at A, breakpoint at B, both automatically continue when hit.
- skneko 6y ago> How are you supposed to figure out that A happened before B in a multithreaded program using only a debugger? Setting printpoints, letting them be hit and continuing... this whole thread seems to arise from the fact people have not learned to use debuggers.
- midjji 6y agoit works, its convenient, its easier to learn, easier to setup, it has less side effects in multithreaded programs meaning you can debug those too. You can even log to a file and then get these logs from your end users... The article does make a good point, errors that only cause failure a few hundred calls after the originating problem are easier to find this way too. Every few years I make an effort to learn to use whatever the current most popular debuggers are, but at the end of the day, its really just very specific kinds of errors that the debugging tools are better for finding and I generally go back to debug outputs soon enough.
- sagichmal 6y agoI've not figured out a way to effectively debug a distributed system except via printf. Debuggers are basically a nonstarter, because stopping one component to inspect it almost always triggers knock-on effects in other components that change the overall state of the system.
- tonymet 6y agoPrint debugging is a tool in the toolkit . It’s good enough for many scenarios , and much easier to deploy most of the time . I still recommend setting up and familiarizing yourself with a step through debugger , but use both
- wodny 6y agoPrint debugging is not that different from setting logging level to DEBUG and those logging calls should already be there in code and give meaningful insight so I don't get printing being often ridiculed. For over ten years of commercial work I used a debugger only a couple of times and in most cases it was against someone else's code, usually when things were completely broken and I needed to get backtraces from multiple deadlocked threads or lacked debugging symbols and things like radare were also required. There were also times when I manually called a syscall using gdb. My opinion is that if you can't reason about the code helping yourself with just a couple of additional messages the code is probably broken/too complicated to begin with and requires serious refactoring. I've never understood people stepping through a program hoping to find some mysterious creature somewhere along a huge stack of calls. In my career I have often seen people always debugging an application as a whole instead of separated modules. Dividing a problem is the key. The same key that allows me to still program using vim without autocompletion, keep APIs sane and coherent, and avoid dead code. One really useful exception is when dealing with electronics. My friends programming hardware use debuggers all the time and in this case it actually makes perfect sense because there is no way to print anything and things like hardware interrupts come into play.
- humbleMouse 6y agoYou’ve only used a debugger a couple of times in 10 years? Yikes.
- wodny 6y agoWhy "yikes"? Note that doesn't mean I don't know how to use eg. gdb outside or inside IDE. My debugging record proves my methods are quite efficient but there's something even more important - there are ways to systematically avoid the need to use a debugger by keeping projects' sanity level high.
- bsder 6y agoActually, using the UART interface to send text breadcrumbs out the port is a standard technique in embedded, too ... The article hits the point of print debugging, you get to see the backward in time. By the time you hit "the problem", the pointer is NULL, the memory is trashed, the system is deadlocked, etc. You need to reason about how you got there. There is a reason why the next step up from basic debugging embedded is "streaming trace"--effectively print on steroids.
- api 6y agoPrint debugging is basically variable watch points, but IMHO easier. The only time to really beware is embedded and real time systems where printing can throw timing way off or cause other side effects. I heard of a case once where printing via JTAG caused an issue due to the power draw of sending all the extra data out. But that was trying to debug a novel board design and its software at once. You won’t hit that kind of thing on normal computers like desktop, mobile, or cloud unless you are writing drivers.
- zmmmmm 6y agoMost under appreciated aspect of proper debuggers is not about the code line of interest but the context they give you about the whole application, ie: the stack frames and their state. When handed a new codebase I often fire up the debugger and attach and set various breakpoints in interesting places and then execute the application to see where / when they get hit. It's a great way to learn a codebase - things that are hard to discover ("when is the database driver created and how does it know its password") just pop out where you might have to spend ages working it out if you were just examining the source tree.
- sudoit 6y agoI'm working on Swift interpreter and the codebase is fairly difficult to debug. There's a lot of reused bits. So if you put a debug point somewhere trying to capture one behavior, odds are that that line will run 10 times for other work before the relevant part uses it. So I tend to write a LOT of print statements that flush of debug variables right before I where I want to debug. Then I set a conditional breakpoint so that I can have the logs "stop" right where I want the program to. Example: // debug print let someValueICareAbout = variable... print(someValueICareAbout) print("") <- conditional debug point here "if someValueICareAbout == 3" I think it's technically still "print debugging", because I'm only using the debugger to stop the program so I get a chance to read my output.
- saagarjha 6y agoWhy not just add an action to the conditional breakpoint that prints the value?
- sudoit 6y agoI’m usually constructing those values just for that one-off test. They aren’t there at runtime. It’s an AST interpreter so sometimes I want to see the value of something that’s 5 properties away inside a syntax node
- da39a3ee 6y agoThis should be a non-debate. A debugger is for when you want to inspect local state in detail. That can indeed often be very useful, and they are sophisticated technology. However, the people who think that a debugger is the only way to debug just aren't good programmers: often you want a picture of the overall behavior of your program. As has been said by someone other than me, a debugger allows you to fix a bug; print statements allow you to think about the right fix for a bug.
- saagarjha 6y agoPerhaps the people using a debugger have it set so it can give them a picture of the overall behavior of your program.
- da39a3ee 6y agoI think that's just playing with definitions. Correct me if you think this is wrong, but for most people in this thread: "debugger" := a thing that pauses and allows you to inspect the local stack frame, and step into/over, evaluate code in the frame context, etc. "print statements" := any technique involving letting your program run to completion and having it output debugging information to screen or file for examination after it has finished.
- saagarjha 6y agoOne of the things a debugger can do is print things once it hits a breakpoint and continue execution automatically.
- asimjalis 6y agoThe reason I like print debugging is that it is repeatable. The debugger requires too much interaction for it to be automatable.
- anarazel 6y agoFor me this isn't an either or. I constantly use both together. For problems that a quickly and reliably reproducible I'll often just use the debugger (if rr is suitable, even better). But there's plenty problems that take a while to reproduce, involve many threads / processes, etc. Where the initial set of potential issues is too wide to easily target with a debugger. There sprinkling printfs around can provide data at a lower overhead than doable with a debugger. Just yesterday I was debugging something where rr didn't finish replaying a workload that originally takes 10s within an hour (loads of io). Switching to print debugging I pinpointed the issue in < 10min.
- domano 6y agoWith the Jetbrains products breakpoint debugging is so easy that i use it for development all the time. Evaluating expressions inside a breakpoint while developing provides many answers in a mich tighter feedback loop, even with go or something equally fast. If i don't have the jetbrains tools i default to print debugging because everything else is too much of a hassle.
- nyanpasu64 6y agoI wish that evaluating expressions in a C++ debugger worked more often. It fails half the time (due to "optimized out") in Visual Studio C++, and 80+% of the time (for various reasons) in Qt Creator, even in debug builds. Maybe it works better in non-C++ languages.
- haolez 6y agoI don't usually resort to a debugger to hunt for bugs, but I use them a lot to explore APIs in "real time". I find them much more convenient than the likes of Postman.
- sunstone 6y agoI learned early on with an expensive microprocessor emulator, just have the code raise the voltage on an IO pin as a print debugger rather than spend days debugging the emulator.
- dmitryminkovsky 6y agoWhenever this comes up, I think of this quote from The Practice of Programming by Brian W. Kernighan and Rob Pike [0]: > 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 https://logging.apache.org/log4j/2.x/manual/index.html
- mumblemumble 6y agoThere's a part of me that wants to say that that opinion has to be taken with a grain of salt and a lump of paying attention to who is offering it. 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.
- cle 6y agoPerhaps it has to do with dynamism? I find myself "debugging" way more in dynamic languages that emphasize interactive development. At that point, the line between running code and debugging gets pretty blurry, since "stopping" execution at some arbitrary place is not very different from normal development anyway.
- LudwigNagasena 6y agoI can just write print(whatever) and get the job done, I don't want to put breakpoints and search for a data structure I need. Why can't I write something like this: breakpoint { debug(var) } ?
- Groxx 6y agoAssuming you mean something like "leverage the debugger to print, so I don't have to do it in code": you can, in most debuggers. This is effectively a "log breakpoint", or a "conditional breakpoint" where your condition is "print(thing)". I use debuggers even for print-debugging for this kind of reason. No need to re-compile between changing prints, just re-run - the debugger session will hold them from previous runs, you can temporarily disable them with a single click, etc. It's FAR faster and more flexible.
- timzaman 6y agoThis is pretty much the only way I debug; - cross language/platform - forces you to come up with hypothesis up front, then test these very systematically - you know what you are doing - debugger doesnt interfere - works across threads and processes
- roca 6y agoAlmost all the reasons people use print debugging can be overcome by improving debuggers --- and to some extent already have been (in the words of William Gibson, the future is already here, it's just not evenly distributed yet). I think it's important for people to understand that the superiority of print debugging is contingent and, for many developers, will not persist. Record-and-replay debuggers like rr [0] (disclaimer: I started and help maintain it), Undo, TTD, replay.io, etc address one set of problems. You don't have to stop the program; you can examine history without rerunning the program. Pernosco [1] (disclaimer: also my baby) goes much further. Complaints about step debuggers (even record-and-replay debuggers) only showing you one point in time are absolutely right, so Pernosco implements omniscient debugging: we precompute all program states and implement some novel visualizations of how program state changes over time. One of our primary goals (mostly achieved, I think) is that developers should never feel the need to "step" to build up a mental picture of state evolution. One way we do this is by supporting a form of "interactive print debugging" [2]. Once you buy into omniscient debugging a world of riches opens to you. For example omniscient debuggers like Pernosco let you track dataflow backwards in time [3], a debugging superpower print debugging can't touch. rr, Pernosco and similar tools can't be used by everyone yet. A lot of engineering work is required to support more languages and operating systems, lower overhead, etc. But it's important to keep in mind that the level of investment in these tools to date has been incredibly low, basically just a handful of startups and destitute open source projects. If the software industry took debugging seriously --- instead of just grumbling about the tools and reverting to print debugging (or, at best, building a polished implementation of the features debuggers have had since the 1980s) --- and invested accordingly we could make enormous strides. [0] https://rr-project.org https://rr-project.org [1] https://pernos.co/about/overview https://pernos.co/about/overview [2] https://pernos.co/about/expressions https://pernos.co/about/expressions [3] https://pernos.co/about/dataflow https://pernos.co/about/dataflow
- db48x 6y agoI’ve said this before, but rr really is a superpower, and Pernosco doubly so. Definitely worth every penny.
- gklitt 6y agoPost author here — just wanted to say I emphatically agree with this and have found your work on rr and Pernosco very inspiring! Anyone who hasn’t seen this work should check it out.
- agumonkey 6y agoMaybe we need a debug oriented programming language ? say `{ ... }?` denotes a scope we want to inspect, debugger gets launched and we get a generic reification of the tree path at that point; with ability to tweak parameters up that path and see multiple new trees rapidly (think Brett Victor live coding) Honestly I think printf debugging is a pity. I do it.. but it feels like processing xml with sed.
- iudqnolq 6y agoRecently I tried out rr, the time travelling debugger. It blew my mind. I never imagined you could just run until an assertions fails, set a breakpoint on the variable the assertion checks, and the run backwards until the last time the variable was modified. Shameless plug: If you're writing rust I wrote a tiny wrapper that finds the appropriate binaries and provides the right config to make it as easy as `cargo rr test my_test`. https://crates.io/crates/cargo-rr https://crates.io/crates/cargo-rr
- bradrn 6y agoOne thing I haven’t seen mentioned here yet: I use print debugging all the time in Haskell, and find it works really well there compared to other languages. There’s a couple of reasons for this, I think: • Nearly everything is immutable, so once I print the value of an expression I know it won’t change in the future. This is not the case in other programming languages, where a variable can be mutated after I print it. • The base library provides a really nice range of functions for print debugging [0] — so I can just wrap any expression I want printed in ‘traceShowId’, and it’ll get printed. (Yes, these functions break purity; that’s why the module is marked ‘Debug’!) Of course, sometimes print debugging isn’t sufficient, in which case I fire up the GHCi stepper debugger. But for the vast majority of cases print debugging works well. [0] https://hackage.haskell.org/package/base-4.15.0.0/docs/Debug-Trace.html https://hackage.haskell.org/package/base-4.15.0.0/docs/Debug...
- jedimastert 6y agoI always feel like I should get better with a debugger, but whenever I'm debugging I always fallback on printf and crashing. It just feels so immediate
- ivan_ah 6y agoThe Python package PySnooper is pretty good for "fancy" print debug statements: https://github.com/cool-RR/pysnooper https://github.com/cool-RR/pysnooper I've caught quite a few bugs using this show-me-all-locals() approach...
- DanielleMolloy 6y agoThis looks pretty neat indeed, thanks!
- Too 6y agoLooks equivalent to python -m trace --trace, which is part of standard installation.
- ivan_ah 6y agoI did't know about this option, and I just tried it. Seems to be waaaaay more verbose, but I guess it can be tweaked/customized. I tried it on a command line script that uses google libs and still waiting for stdin to come back... What I liked about pysnooper is the ability to snoop on a specific function and/or code block to focus on the work-in-progress section of code.
- mjrbrennan 6y agoI don't get the usefulness/effectiveness print debugging. I work in Ruby and JavaScript and I find it much more efficient to know the whole state of the world and the objects in it at a certain place, because I generally know where the problem may be. For example I use pry in ruby and the debugger; statement in JS. Maybe it is just the way my brain works? I'd rather stop and see what I need behind a condition than have to filter through a lot of possibly unformatted console output.
- mybrid 6y agoThere is a dimension that gets overlooked in these discussions: tests. Every bug should start with a mind set to create a new test: unit, integration, or end-to-end. These are regression tests. Now, whether the test is needed or not is a decision that will fall out the bug fix. There is a distinct difference between the skill of debugging and skill of writing tests. I focus most of my efforts in writing test code. Someday perhaps IDEs will be the test platform for all the test types. That's not today though. The question in my mind is not print debugging versus IDE, but test code debugging versus ad-hoc debugging. IDEs encourage ad hoc debugging because once a bug is fixed, the test code needs to be written from the ground up, a step this is often left out due to time limits. I debug in test code and when the debugging is done the test is written. This applies to new code as well and mirrors the paired programming notion of starting new development using test code.
- mybrid 6y agoIDEs are amber and throw away effort. All the break points, data integration and what not is thrown away after the bug is fixed. Further this IDE effort is not shared between developers. Log debugging is reusable out of the gate. Log debugging can easily be promoted to a production statement if deemed important. IDE developers it seems to me need to work on how all the time and energy IDE developers spend on a bug can be generalized to the point these things can be shipped with the code itself. Until then it is throw away work trapped in the amber of the IDE.
- flakiness 6y agoThis feels like an instance of "worse is better". It works well enough, it is easy to start, it is robust, and it is naturally integrated into your workflow (which is, run the code you wrote.) Debuggers are like a perfectionist approach, and still lacks things like timeline-like view that the articles mentions.
- euske 6y agoPersonally the biggest obstacle of using a debugger is that it cannot be automated easily. You have to be present when it's triggered. You have to navigate it manually. When the program crashes again, you have to repeat the process. I know some debuggers can be automated but then you'll have to debug the debugger script. Logging is much nicer because you can turn the exploration process into a text analysis problem. Logs can be searched, stored and compared. For me, sifting the log is much easier. Whenever I try to write a medium-sized program for a serious kind of purpose, the first thing I do is to set up a nice and reliable logging system. This is the decision that you won't regret for the rest of development. I would argue that the use case of a debugger is much narrower than logging/printf debugging.
- elonvolo 6y agoI wrote my own print debugging tracer. It’s now my goto for debugging most things. https://github.com/elonvolo/logitall https://github.com/elonvolo/logitall
- invalidname 6y agoYou can already do print debugging in runtime using tools such as Googles stack driver, Lightrun and Rookout (probably others too). These tools let you inject new logs into your running server so you can do print debugging without redeploying a cluster. Pretty darn cool. They also let you place a breakpoint which doesn't stop execution so you can get a stack trace, variable states etc. without the pain.
- yongjik 6y agoI find it mildly disturbing that so many comments are saying "But breakpoints!" One would assume that anybody who used a debugger for more than a day knows about breakpoints. TFA isn't saying you have to step through every line in a debugger. It's saying that, even if you employ your amazing debugging skill to find exactly the point you want to look at, you will only be looking at that exact point in execution, and not other points at the same time. Sure, it will be a very detailed representation of that particular point, which can be extremely handy, but sometimes you want to look at a hundred different points in execution, at once. That's when printf comes handy - you just need a large monitor (or a small font and good eyes).
- kaba0 6y agoThen I would say that logging is useful. But stack traces in debuggers, conditional break points and stopping at exceptions and the like are the best.
- bitwize 6y agoIf you think print debugging is "unreasonably effective", it's probably because you have a shitty debugger. Try Visual Studio under Windows. Go on, try it. You'll be surprised at just how stone-knives-and-bearskins the standard tools on Linux really are.
- cryptonector 6y agoI'm shocked to see no mention of DTrace in the comments.
- jmrm 6y agoPersonally, using a good debugger and knowing how to use have been more useful to me that anything else. I mainly code in C and C++, and Visual Studio integrated debugger and GDB are my main debuggers (depending what I'm doing). For me is faster to double click the border of a line in VS or writing "break 123" or "break fooFunction" in GDB and stepping and watching how some values changes than adding and removing "printf" lines. Adding some asserts are other thing. They always are good, and often necessary to find some "Heisenbugs". In other languages I probably won't think the same, but I haven't done anything big enough outside C or C++ to give a proper opinion.
- sandos 6y agoI love tracepoints, which is basically print-statements but dynamically from he debugger. Sadly I almost always end up in having performance problems, so I still need to an if or so to the code for the tracepoint to perform well. And then were back at printf debuggging again...