23 ms·
“I think the vast majority of developers still debug using print() statements”
- FatalLogic 4y agoPlease be aware that Paul Graham's original tweet, which wouldn't fit in the title, is: "I think the vast majority of developers still debug using print() statements." — email from a founder
- rektide 4y agoI had a good long while where I made the debugger my habit & used it well. I have fallen off the path. Part of the problem is I often am looking at interactions across systems, so just having a bunch of logs is helpful. Versus having a bunch of debuggers open. Learning the logs also helps me get better at debugging prod: using what's available. But I also add in a bunch of random `console.logs()` too. I'm looking forward to tracing becoming a more regularly used tool. Being able to see a timeline, across services, is hugely powerful. I hope some-day we design languages that either integrate tracing, or, ascending into high-fantasy, languages where the runtime itself starts to be powered by tracing-like structures, where the tracing also serves like event-sourcing for your galaxy. Innovating on language capabilities, rather than just ever-struggling with language syntax & capabilities & types, feels vastly under-explored.
- brundolf 4y agoI do, mainly because of the cognitive overhead of setting up (and fixing when necessary) the extra tooling. Integrating my editor with whatever runs my code is just rarely a zero-effort experience The exception is with JavaScript in a browser, since the browser is both the native environment where the code is running, and can show the highlighted code and let me set breakpoints. However, even then, more and more of my JS code takes the form of expressions, not procedural statements, and breakpoints just don't help much with those. So in practice I still use them rarely
- syspec 4y ago> However, even then, more and more of my JS code takes the form of expressions, not procedural statements, and breakpoints just don't help much with those Would you mind expanding on that?
- brundolf 4y agoReact-style code ends up being more functional, with a lot of just nested expressions, and the concept of a "break point" barely even makes sense in those contexts. If you want to see intermediate values you'd have to first break them out into named constants, and even then there'd be little point to "stepping" (vs just printing) because they're going to be stateless I might use a stepping debugger on the odd procedural/stateful code, where there are actual variables that change over time, but that's less and less common for me these days
- bitlax 4y ago“The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.” — Brian Kernighan, “Unix for Beginners” (1979)
- MonkeyClub 4y agoThe evolution of a word while it remains still is interesting here: Brian's 1979 "still" is a whole world away from Paul's founder's. Still, it remains easier to print a value than to reach for gdb. But once you have gdb going, it is easier to do in depth fine surgery with gdb than littering your code with printfs. Now it seems the two worlds are entangled and I'm confused. C-x #n
- greenyoda 4y ago> Brian's 1979 "still" is a whole world away... Indeed. In 1979, the debugger in Version 7 Unix, called "adb", was quite primitive by today's standards. For example, you couldn't even set a breakpoint at a specific line of a C function, only at the function's entry point. For the "adb" documentation, see Volume 2A of the Unix Version 7 doc: https://plan9.io/7thEdMan/v7vol2a.pdf https://plan9.io/7thEdMan/v7vol2a.pdf On page 314 it says: "C does not generate statement labels. Therefore it is currently not possible to plant breakpoints at locations other than function entry points without a knowledge of the code generated by the C compiler." I.e., you'd have to delve into the machine code to find the address of the first machine instruction of your C statement if you wanted to put a breakpoint there. Debuggers have come a long way since then.
- cyanydeez 4y agoI do because chrome debugger doesn't recognize that because I'm using. Bundler, I don't care about the mess of layers between my code and my errors. Everytime I try to step through, there's all these arbitrary access layers that have to be hit.
- jraph 4y agoTry configuring your bundler so it outputs source maps, and disable js transformations aimed at compatibility with old browsers. Like whatever they do with for..of loops and async/await that makes the code unintelligible. For this, put whatever is needed in the configuration of the bundler to target the latest version of Chrome or Firefox (for instance [1] for babel / webpack). It's worth it really. And maybe in the future bundlers will not be needed anymore. On recent browsers with HTTP2 and gzip compression, I question the need of bundling at all, especially if the dependency tree is limited (yes, that's a HUGE if). With HTTP2 multiplexing / connections kept alive, Good old <script> tags might work quite well now (with type="module"). [1] https://webpack.js.org/loaders/babel-loader/#customize-config-based-on-webpack-target https://webpack.js.org/loaders/babel-loader/#customize-confi...
- hoten 4y agoSounds like you want this? https://dilshankelsen.com/ignore-library-code-while-debugging-browser/#:~:text=If%20you%20know%20upfront%20which,browser%20like%20Vivaldi%20or%20Edge https://dilshankelsen.com/ignore-library-code-while-debuggin....
- jraph 4y agoThis is often laughed at, but I think this is fine. Whatever helps debugging efficiently. And debuggers have their uses too. Printing is my usual way of debugging. I use / switch between various languages that have unequal support for interactive debuggers with different interfaces, in various contexts where debuggers are not always usable. Printing / logging always works the same regardless of the language and the context. Step by step often looses me, and print forces to think about what and where to print so it also has its advantages. Interactive debugging has a big advantage: no need to recompile / reload each time you need to check out the value of a variable at a new location in the code. But yes, the fact I switch between languages and contexts seems to encourage me to use generic tools that work okay for every language and context instead of specialized tools, so Kate and printing it is. But you can become efficient anyway, and for a task where printing is not enough, you can always spin up the heavy tools.
- horsawlarway 4y ago> But you can become efficient anyway, and for a task where printing is not enough, you can always spin up the heavy tools. this exactly. Print based debugging is a great way to verify all sorts of assumptions, and it gives a wonderful order based view of execution very quickly. If that turns out not to be enough - by all means, break out the real debugging tools, but I'd wager 95% of the time, print based debugging will lead you to the answer more quickly and easily than spinning up gdb or inspect-brk or visual-studio or any of the other heavy weight tools. Not that those tools aren't incredible (I mean that so literally) - it's just that they're like bringing a back-hoe in to plant your tulip bulbs... it's complete overkill for answering questions like "Did this code even run?" or "how many times is this function getting called" or "is function a called before function b" and SO many other useful basic tests for my assumptions.
- nunez 4y agoAgreed. I forgot how to use gdb and windbg because it's been so long since I needed to grok assembly to troubleshoot something (you need to use gdb with Go too) and most of my work these days is with interpreted languages that are easy to reflect into, but its very useful knowledge to have in the toolbox.
- 4y ago
- naniwaduni 4y agoOften they're even right to do so!
- quadcore 4y agoWhy would you debug by hand when you can use a computer? Meant in favor of print of course. Thinking as I write it probably means it's the print statement that should be improved for debugging if anything. Imagine a debugger that collect them, with the memory / variables and stuff. Could also be turned into break points. Alternatively, could a newly developed terminal be coupled with a debugger like that?
- rascul 4y agoSometimes I'll just log damn near everything with with debug! from rust's log crate so if I need to debug I can just change the log level and I don't need to remove it later (except for sensitive data).
- unixhero 4y agoI do use print yes!
- fsociety 4y agoI find the distinction between step debugging and runtime debugging to be more useful. Someone who steps through every line of code will probably take longer to find the bug, and will spin cycles on rabbit holes and false paths. Someone who uses “runtime debugging”, or rather has a hypothesis on the bug or what a piece of code is doing and uses a debugger or print statement to verify that will find the bug much quicker. With this mental model, you can see how print statements can be as effective as a debugger in many scenarios.
- thirtyfivecent 4y agoTotally agree. Print statements are windows to the programs internal state. They can be activated during specific times, under certain conditions, can group together information and values.... The name "print statement" does not do them justice , that is the problem i think, they are a conduit, a channel of symbols for peeking into the program
- josteink 4y agoSo can a debugger, except when you find something unusual, a real debugger will allow you to inspect things you didn't consider instrumenting, change variable-values, re-run code and validate assumptions. I just don’t see where this skepticism against specialised tools for tracking down bugs comes from.
- Dudeman112 4y ago>I just don’t see where this skepticism against specialised tools for tracking down bugs comes from Probably from the previous skepticism against print debugging. Every prevalent strong opinion in a social setting is eventually met with an equally strong opposite opinion in the same social setting.
- dexwiz 4y agoIf your runtime is in any sort of framework, step debugging often drops you into framework code that you never look at that exists between your logic. This is my biggest hang up when trying to step debug beyond a single method or class.
- s-xyz 4y agoI think its fine to use print in some cases, e.g. quick debug. Google cloud function logging actually even translates it to an info record if I am not mistaking.
- beninsydney 4y agoI have always loved this method of debugging because printing messages outputs a chronological summary spanning the entire execution, and only relevant information is included.
- sph 4y agoI used to debug frequently when I coded on Win32 GUIs 20 years ago and running the debugger was as simple as pressing F5 instead of F9. Nowadays, with a multitude of languages, setups and editors, I don't even bother. Even setting up JS debugging under VSCode, which supposedly is a core feature, has always been an exercise in frustration every time I've tried it. Rarely I run gdb when I'm debugging a particularly hairy C issue, but with any other language I don't even bother and add prints everywhere. The JS "debugger" statement is pretty useful, but it's still very awkward compared to the golden standard of Microsoft IDEs from two decades ago. Don't blame the users, blame the tooling, editor and language developers that never cared for delivering a good debugging experience.
- SergeAx 4y agoAt least Go debugging in Jetbrains' Goland is a smooth and effective experience, on the other hand. I think their other language IDEs are more or less on par.
- RcouF1uZ4gsC 4y agoI too got spoiled by Visual C++’s debugger. Nothing on other platforms combines the power and ease of use.
- anamax 4y agoVisual C++'s debugger is so good that it changed how I wrote code. It let me debug things that I couldn't debug with a lesser tool. That's why I quit using it and went back to debugging with print. Print-debugging limits my cleverness and forces me to think about what's going on.
- DeathArrow 4y agoDebugging C# in Visual Studio is at least as good.
- carlmr 4y agoVisual Studio is plainly the best IDE I've ever worked in. It may be sluggish at times, but whether I was coding in C#, C++ or Python, autocompletion, debugging, and refactoring always worked really well. The only problem is my current projects are all on Linux.
- Noumenon72 4y agoI'm at my least productive when I can't use a debugger (Jenkins jobs, nginx config, networking stuff). Everywhere else, getting it set up is my top priority. * I usually fix the bug right there in the debugger where I find it. It's so much easier to fix logic in a REPL with all the actual variables available then in a slow guess-and-rebuild cycle. * Being able to inspect complex objects is so much more convenient when you can right-arrow your way through the levels or watch a nested subpath. * I'm constantly surprised and confused as I make my way through code. Reality contradicts my assumptions at every step so I need the ability to see that to find my way to the bug at all. * I love log statements, but I typically need the debugger to find the place to add the log statement. There are so many candidates I'd have to add five prints for every one I use.
- hamburglar 4y ago> I'm constantly surprised and confused as I make my way through code. Reality contradicts my assumptions at every step so I need the ability to see that to find my way to the bug at all. This is a huge point that a lot of people miss. When I was a junior dev working on some shrink wrap software, I’d submit a change for code review, and my stickler boss would read it and then say “have you single stepped through the main path of this code yet?” I found this annoying, but he made me do it, and god damn it if I didn’t often encounter situations where the code apparently worked, but I caught subtle errors by watching the state change one step st a time. “Oh my god, this value is never even looked at and I’m getting lucky it never fills the buffer on subsequent iterations!” It’s very, very eye opening. It became clear that it’s hubris to read a piece of code and assume I can interpret it faithfully. The debugger gives you another picture of reality.
- marssaxman 4y agoSometimes the print statement is the best tool for the job; sometimes it is the only tool for the job. If you know how to debug by adding print statements, running a test, and reading the resulting log, you can solve any problem, in any environment, on any platform[1]. Other tools may well provide faster results in specific situations, but one has to balance the time spent learning & managing fancier tools versus the time spent getting the job done. 1) Well, okay, there are exceptions. Perhaps you have to find a spare IO pin attached to an LED, and make it blink at you in morse code, because your serial port hasn't come up yet, so you can't print to the console; but it's the same idea.
- rurban 4y ago
- boardwaalk 4y agoThat's a high horse you've got there. There is a vast class of bugs that are not memory bugs and many languages where memory bugs are just not an issue.
- hgomersall 4y agoNot only that, but the bugs I find most difficult to deal with (like races) I find have a tendency to disappear in a debugger. Tracing output is what's needed here, and that's just a slightly more structured print.
- userbinator 4y agoI have debugged plenty of memory bugs without using a debugger. Debugging multithreaded/async code is an entirely deeper level of hell, and there's been plenty of times where just having a debugger attached was enough to make the code work, or building unoptimised/with debugging information. On the other hand, carefully placed prints can catch the culprit without affecting the timing so much that the bug disappears.
- Cerium 4y ago
- Beltalowda 4y agoI like printf-debugging because it's usually the fastest and has the least mental overhead. Even with IDEs and integrated debugging I find there's often a lot more overhead. And when I do use a debugger I always just use it to inspect variables; quicker to just print those same variables: less steps, and especially works better if the same code will be run 10 times and I want to print those vars 10 times too. I never found stepping over code useful in a debugger, and I tried to use it many times because surely it's useful for some, but turns out it's not for me.
- nunez 4y agoAh, yes, printf(), the tool of choice when you have no tests, thousands upon thousands of lines of code, and millions of live wires
- mbrodersen 4y agoI have an extensive test suite (10000+ tests) and zero bugs in production for the last 5+ years. I never use debuggers. Why manually do what software can do automatically? That doesn’t make sense.
- musicale 4y agoLogging is incredibly useful and scalable - I'm not surprised that people still use it.
- darepublic 4y agoDebugger in chrome dev tools is easy enough though as I get older I find myself actually preferring console log, or similar strategies, again. Especially if the function in question get invoked multiple times, printing seems easier
- masklinn 4y agoNote that all modern browsers should have “logging” breakpoints, aka breakpoints which don’t actually break and just evaluate an expression.
- deleted 4y ago[deleted]
- pull_my_finger 4y agoI don't understand the cleverness here. How else would you start debugging? If you're not a IDE user, print is your first course of action. This is like gufawing about a doctor using a barium swallow or an x-ray.
- whatsakandr 4y agoI've found tests to be my new print debug. Too complicated of an internal state to get at what's going on? Write a test. The failing test will tell you the output.
- aqme28 4y agoPlus you don’t have to delete it when you’re done, like you do with prints or breakpoints.
- jakebasile 4y agoI'll stop using `println` when it stops being so effective.
- kalleboo 4y agoWhen debugging Xcode projects I 100% use the debugger - even when doing print debugging (for e.g. hard to pinpoint UI bugs) I'll use print breakpoints since you can add/remove/edit those without recompiling/relaunching. When using something like JavaScript or PHP where the tooling is finicky and fragile I just give up and use 100% print debugging.
- interpol_p 4y agoXcode has really great debugging, though the debugger can be slow to initialise on the first breakpoint I love that you can hit spacebar on any local variable and "QuickLook" preview it just like a file in the finder. It even renders UIViews, and previews 3D models right in the debugger. The 3D view debugger, memory graph inspector, and all the other tools are just great
- heretogetout 4y agoOnly the first breakpoint? It's glacial for me on a pretty well spec'd 2019 MBP. Every step takes several seconds as, I guess, resources or just data are pulled from my device. I still try to start with the debugger but I almost always fall back to print statements. (It's often faster to add print statements and rebuild+redeploy than wait for a step or two in the debugger).
- kalleboo 4y agoThe QuickLook is also amazing for Core Image rendering flows, where it generates a PDF showing every filter/rendering step!
- cagey 4y ago99.9% of the time (long-term personal C++ project) I use printf-based logging to root-cause unexpected behavior. I use gdb only to root-cause segfaults (which are thankfully quite rare). I use GCC exclusively, and its type-checking of param types vs format string largely prevents me from spending time in gdb; pre-GCC adoption, the biggest source of segfaults in my world was printf type mismatches. Addendum: I'm sure it violates every coding convention ever created, but, in almost all cases, once I add a debug printf to my source code, for example, ED && DBG( "%s", foostr ); // ED is usually a function-scoped enum normally ==0 I leave it there forever. This allows all DBG's to be eternally subject to GCC printf param type build time checking going forward (preventing the debug print code from rotting (as it absolutely would if commented out), at minor maintenance cost (typically only when I switch to a new GCC release)) while not imposing any runtime cost (unless I flip ED to 1 and rebuild to enable function-specific detailed logging). As far as this practice injecting large amounts of comprehension-thwarting noise into my source code: all such `ED && DBG(...);` code sequences are positioned in their own column far to the right of the non-DBG code (needless to say, I don't subject myself to 80-column coding-convention limits; more like 160-column). That this results in multiple statements per line (another typical taboo) is a don't care, since I'm never using a debugger to single-step thru code, and in any case, by convention my DBG() calls never modify program state. It works great for me, but probably not for anyone else.
- Gibbon1 4y agoThing I do in my embedded code is trap seg faults and then use some magic to pull the offending instruction address off the stack and store it in no init memory, then reboot. On startup I check and spit out and log the address. addr2line 99% of the time can tell you what line of code was a bad boy. Like you I also leave debug printf's behind. I find that they also function as comments about what's important and likely break. Unlike everyone else I've seen I have a command line interface I can use to poke at the program while it's running.
- cagey 4y agoI too did embedded code (firmware) work, in the 199x's with only an ICE (basically, hardware debugger). I only dreamed of having printf-capable logging facilities on the HW target; almost all of the firmware "game" comes down to finagling the egress of debug (and application characterization) information off the (in my case, eternally extremely cost-/resource-constrained) target. These days things are much better in this regard (32-bit CPU's and associated memory and other-infra (data-path) resources are MUCH more capable than those of the 8- and 16-bitters of a few decades ago...). Briefly returning to the thread topic: this experience doing FW dev and usually only having a debugger (and how terribly inefficient that was) strongly guided me to a logging-based debug approach: the ability to collect a log and filter it using ad-hoc (e.g. Perl-based) tools, provides IMO an order of magnitude more powerful way to truly understand program (and HW target) behavior compared to a debugger. I didn't mention earlier that a key component of my "leave DBG printfs in the source code and let the compiler both (a) type-check the trailing param types vs the format string, and (b) elide codegen (executable file footprint) for those same DBG printfs due to the compiler optimizing those calls out" approach is that I build with -O3 which (at least historically; I haven't checked recently) provides a poor gdb debugging experience because the optimizer frequently de-correlates source code lines from generated machine code; if I encounter a segfault, I must suss out a repeatable test case, clean rebuild with -O0 (no optimization), keep fingers crossed that the test case still fails (it usually does), and repro under gdb. The hassle associated with this gyration inhibits my debugger use in non-segfault cases (though these days a clean rebuild is a lot faster than it used to be).
- pjmlp 4y agoI only use print statements when left without any other option, and am thus forced to use them. We have so advanced debuggers, OS telemetry, events, that plugging into print statements as first option reveals either lack of tooling or education.
- wruza 4y agoMany process managers [can] use stdout as a logging interface today. It is pretty useful when you can see debug logs regardless of how you run it.
- userbinator 4y agoWe have so advanced debuggers, OS telemetry, events No one wants to learn about all that crap when they already know how to easily output from the software they are developing. I don't want to be debugging the debugger or logger or whatever else.
- pjmlp 4y agoSo it confirms, lack the education.
- mbrodersen 4y agoWhat an arrogant thing to say.
- mbrodersen 4y ago25+ years of professional experience here. Having worked in embedded systems, complex Enterprise systems, high speed compilers, AAA games, Client/Server applications, Web development etc. I never use debuggers. The software I am working on is too complex for that. So perhaps you are the one who needs a bit of education to realise that what is true in your context is not true in all contexts. When was the last time you used a debugger to fix event interaction bugs in distributed systems for example? How about real-time timing sensitive software? I know your answer is “never” because you can’t debug those systems using a debugger. print() debugging is vastly more powerful than interactive debuggers.
- arlcode 4y agoI find myself using trace / debug level logs quite extensively. Not only does it add more comments to critical parts of the code. It also allows me to enable them briefly in production or staging if needed. Actually debugging code happens rarely these days as it's usually unnecessary. PS: Copilot makes writing logs a breeze for me and taught me a trick or two about formatting.
- rramadass 4y agoVery True. The reason is that the Developer is Reasoning through the Code and Playing Computer simultaneously thus forcing him to make sure that both thought streams are "synchronized" at the stage of the debug print() line statements. It is this upfront work which goes into figuring out what the Developer intended vs. what the Computer really does that is so valuable for Code Comprehension.
- whacked_new 4y agoKovid Goyal, the author of calibre, has this in the official developer documentation [1] Using print statements This is Kovid’s favorite way to debug and he's gotten really far with it. Granted, he is not the average developer. [1] https://manual.calibre-ebook.com/develop.html#using-print-statements https://manual.calibre-ebook.com/develop.html#using-print-st...
- pwdisswordfish9 4y agoYes, an ego this massive is not found all that often. https://bugs.launchpad.net/calibre/+bug/885027 https://bugs.launchpad.net/calibre/+bug/885027
- petepete 4y agoAlso the creator of Kitty, the best terminal emulator around.
- tester756 4y agoIt's probably matter of ecosystem, tooling If it is weak, annoying, then people use prints When using C# with Visual Studio and its strong debugging tools, why would you want use prints? (except specific cases that require it) You can modify code at fly, jump between lines, evaluate expression at fly, use conditional break points Why bother with some prints?
- deleted 4y ago[deleted]
- oreally 4y agoThat's because many programming environments have not developed their debugging tools and people have accepted it as the mediocre norm. If you're ever tried an integrated IDE debugger for say debugging variables, I don't think you can possibly say print debugging is better over one keystroke for setting a breakpoint, one keystroke for running the program to the breakpoint, and one gui window that just displays the local values.
- wruza 4y agoWell I can. I’ve used debuggers from my MASM “era” to TP to TC to VB, and then dropped that habit in favor of debug logging entirely. I’d say that if you ever tried an environment where you can have that black text window and dump there whatever you want to, all your I-D-Es start to fidget nervously. But YMMV. print debugging is better over one keystroke for setting a breakpoint A breakpoint is by definition a point in your process where you can inspect things. You can’t inspect the entire path without tediously stepping through it. With debug logging you can see the path, but have to pick what you want to see to not overwhelm yourself with output. For me the latter yields much better results in general.
- oreally 4y ago> I’d say that if you ever tried an environment where you can have that black text window and dump there whatever you want to, all your I-D-Es start to fidget nervously. But YMMV. Well these are environments that don't allow nice code editing and debugging. So you've got to work with what you have. Doesn't make it any nicer. Certainly doesn't justify printf debug over other debuggers. > With debug logging you can see the path, but have to pick what you want to see to not overwhelm yourself with output. For me the latter yields much better results in general. Are you seriously suggesting that the time and brainpower it takes to type out the code for the prints for the entire code path, AND figuring out when the logic is for the prints happen is better over a debugger stepping through the code path? This is one of the least meaningful things a programmer could do unless they love typing for the sake of it. It's what I meant by a mediocre norm - you like coding but forget the end goal of it, so you heap lots of unimpactful management layers on top.
- bpye 4y agoI'll use whatever works best for the problem. I normally start by trying to step through in a debugger, but often that is too slow so I'll introduce some extra prints and/or tracing - and try again. Conditional breakpoints might work for some folks but when doing kernel level development I've always found them too slow to really be useful. The biggest issue I have with prints is that I need to recompile, deploy my change and reboot - probably getting towards 10 minutes, whilst with a debugger I can inspect additional state without any work, and some times even do some limited patching to fixup variables or replace something with a NOP.
- YZF 4y agoI recently found that this is a religious topic on the level of vim vs. emacs or tabs vs. spaces. Some people rely heavily on debuggers for their day to day coding. I learnt to code without debuggers or IDEs - there weren't any source level debuggers. Then embedded/microcontrollers where the tooling existed but was very expensive (ICE etc.). I definitely used debuggers more at some point DOS/Windows Borland and MSFT IDEs but mostly as time went by I found that I can usually just figure out the problem without needing a debugger or write correct code in the first place. If you're working on a large distributed system your traditional debuggers don't really help that much any more. But sure, there's situations where you'd still use one. I remember using watches like decades ago and then I just didn't need to any more... Whatever works I guess?
- Findecanor 4y agoIn larger projects I have more commonly used logging-statements, each with its own log-level. Changing the log-level to log more is almost equivalent to using printf()s, only that you will get many more log-entries to wade through. But often that's fine: it is more annoying to forget to log something than to log more than necessary. There is typically also both compile-time and run-time log-levels, so that you can disable logging in release builds but still keep all in the source code.
- hysan 4y agoUse the right tool for the job? For quick sanity checks and bugs that you are pretty sure about, sometimes it’s just faster to chuck in a print statement. Or if you want to just quickly see a stream of info at different points, throw in a bunch. Other times, you need the control and context provided by a debugger, so use it then.
- phendrenad2 4y agoThe only people surprised by this are non-developers. Founders are apparently very shocked that developers black terminals full of scrolling text and arcane key commands are used... to write printf() statements.
- praptak 4y agoI certainly hope so! Printf debugging makes you think about the program, build hypotheses and test them. Stepping through the program is hit and miss.
- jl6 4y agoI wager most software projects start as small scale experiments where expedience rules. Printing to a console is perfectly good at this scale - and many such projects never make it beyond this scale. Tools like debuggers and structured logging can be introduced later, after it becomes clear that the project has ongoing value and the investment in extra tooling is worth it.
- mbrodersen 4y agoI maintain very large complex software on a day to day basis. I never use debuggers. Debuggers doesn’t scale to solving large complex problems. Especially in distributed systems.
- wruza 4y agoIn my experience, debuggers are only useful in stepping over someone’s code. In your own code, a logging^ output is simply faster to read and reason about. It is like a trace of debugger, but you don’t have to set up sessions with different watches and conditions every time you start a project. I rarely want to “step”, I want to see the path. That “still debug using print()” part is misleading. Debuggers suck by design. Loggers are the better debuggers. Also, if you don’t log and someone asks you “what was that yesterday” (not necessarily an error), good luck with your debugger-only approach. Logs are everything - ask your sailor, pilot, vehicle maintenance guy, credit manager. If I was forbidden to log and was asked how to make a debugger better, I’d say that it should use #preprocessor directives right in modules, in which I could set up watches, object dump conditions, i.e. essentially logging. Please realize that at the end of the day all debugger does is printing something conditionally. But then why not just use plain ifs and prints (and few “if (DEBUG_ENABLE)” in case you want to omit it from the executable). ^ doesn’t matter if it is console or some service, as long as you can see it in real-time
- hamburglar 4y ago> at the end of the day all debugger does is printing something conditionally This ignores an awful lot of nuance. If I’m stepping through an algorithm interactively and deciding on the fly what I want to pay attention on a given iteration (or which iteration I want to start paying attention at), or running until a particular thing of interest changes, or any number of debugger scenarios, you can argue that logging all such info all the time and then poring over the output in a log technically gives the same information, but at that point you are positively drowning in data. A debugger gives you the ability to dynamically change what you care about “logging” as your idea of what you need to know changes. I feel like nobody who has ever ever experienced a truly fast (perf-wise) and fluid debugging experience would ever deny how nimble it feels and how you’re never going to get there by deciding ahead of time what is important to log (or by logging absolutely everything). I also think a lot of people are tainted by the debugging experience in a lot of more modern languages. Golang, python, even Java.. the debugger perf is miserable. Give me msvc 6 and three Fn keys and it feels like you are controlling the processor in real-time.
- mrwh 4y agoI know I do. And I know I used to feel a bit guilty about it, that a real software engineer would use some fancy debugger. There are plenty, I work at a FAANG. But as others have pointed out, printf is often the best approach. It's iterative and repeatable. I don't feel at all guilty that's it's my go-to approach now.
- DeathArrow 4y agoI don't think using printf is wrong, but if you can use a debugger, you will solve the bugs faster and with less frustration.
- kevin_thibedeau 4y agoFor embedded, a debugger can interfere with ISRs and thread execution and printed output is often the right tool for the job. Just think of printed status as console logging and ignore the snooty crowd who don't know any better. None of them bat an eye at the inefficiencies of slinging around the text encoded data structures we're inundated with. There's a healthy dose of hypocrisy in the industry.
- maccard 4y agoIf you think prints can't interfere with thread execution you're very, very wrong.
- rcxdude 4y agoIt is substantially less interference than debuggers though (especially if you use async logging). On a lot of embedded systems hitting a breakpoint means the system will not function when execution continues.
- maccard 4y agoI don't work on embedded systems so I can't comment on that particular kind of work. Interference is still interference though, and while debugging things that are susceptible to this kind of interference you need to be very clear about what you know you can rely on and what may be wrong. Relying on printing to not interfere is not possible. At best it's more reliable, in my average experience it's about as reliable, and in the worst case it's the same and both are broken. > especially if you use async logging If you are async logging then pretty much all of this goes out the window though. Firstly is your formatting async or does it happen when the message is enqueued? Message formatting is expensive, and the longer the message the more expensive it is to format. If you're dealing with low performance devices, you can definitely expect this overhead to adjust the performance and timings of your code. Running printf("%s\n") on a 1MB JSON blobis going to mess everything up, for example. Secondly, assuming that problem is resolved, you still need to order the messages to be printed. There is absolutely no guarantee that two messages sent at the same time from two different threads will be printed in the same order unless you enforce that through some other primitives - at that point see above.
- DeathArrow 4y agoWhen I can run the stuff on my machine, I use a debugger because it's much better to being able to step through, repeat execution of some sections, inspect values of any variables in the scope, change values of the variables. But since some bugs will only appear in production, and I can't deploy in production a development build, nor can I connect with ease a debugger to a process running in a Kubernetes pod, I have sometimes to use logging for debugging. Those cases are very rare, but they might still happen.
- kgeist 4y agoStepping with a debugger is OK as long as the piece of code you are debugging is more-less single-threaded. If your system is event-based with a ton of background processing, I found that debugging via logging is much simpler to set up and use in that case. It also blurs the line between debugging a problem locally and in production (including post mortem), the skills somewhat transfer. I also often use print() directly because to use a proper logger, I'd need to inject loggers with DI across several classes, import packages, when a simple print to stderr would work as well.
- maccard 4y agoWhy do you think print debugging is going to help in a multithreaded scenario? Either your output is going to be wrong because there's no synchronization, you implement buffering which drastically changes the timing of your code and the behaviour, or you lock for printing which again drastically changes the timing and behaviour.
- kgeist 4y ago>you implement buffering which drastically changes the timing of your code and the behaviour Well, if slightly different timings changed behavior, I'd consider it a bug. The microservice systems I usually develop have strong guarantees of processing order via work queues (first in, first out), various stateless components communicate only through channels/queues (shared nothing) and different timings from run to run are absolutely expected because of the unpredictability of the network and external components. The rate of processing events in the queue, when debugging locally, isn't usually too high to "break" the output (i.e. when it's interleaved from multiple workers), even though it happens sometimes, but only if it's in-process. If a thread of execution is its own container instance, they'll have their own stderr output attached, so there's nothing to synchronize. I understand what you're talking about, something like audio/video processing software or gamedev would suffer from printing as it would mess up the timings. But I'm not sure if stepping with a debugger is any better in that case.
- maccard 4y ago
- elwell 4y agoEmacs + Cider Debugger works great for Clojure.
- bogota 4y ago… because it works and its faster most of the time.
- demindiro 4y agoI don't think there is anything wrong with "printf" debugging. I actually use Rust's dbg!()[0] macro quite often since it is quick to add and it gives me a good idea of the program's flow of execution. Doing the equivalent with a debugger is much more tedious IMO since you need to set up all breakpoints and then either manually continue after each breakpoint or configure your debugger to print something at each point. Usually when I use a debugger it's after I already narrowed the problem down to a specific section and I need to view more state than dbg!() can practically show me. [0]: https://doc.rust-lang.org/std/macro.dbg.html https://doc.rust-lang.org/std/macro.dbg.html
- ProfXponent 4y agoI really cannot believe what I am reading in this thread. It’s pretty trivial to setup a debugger in most IDE’s these days and share a config with your team. Print logging falls flat on its face in async systems. I feel like the ven diagram of people using text editors as a replacement for IDE’s and people doing print logging must be a circle.
- wruza 4y agoWhat do you mean by falls flat?
- nextaccountic 4y agolibs like https://docs.rs/tracing/latest/tracing/ https://docs.rs/tracing/latest/tracing/ (for rust, but i'm sure most languages have things like it) can make logging work for async programs. it achieves it by structuring the log in nested spans, instead of just appending a timestamp and thus losing which async task is related to which log line. alongside with https://github.com/tokio-rs/console https://github.com/tokio-rs/console it makes log-based debugging for async programs a breeze (tokio-console shows which async tasks are running, like a top(1) but for async tasks, and can show logs of a given task) it's really fundamental to have good logging! both to discover issues as they unfold, and to debug past runs of the program for which you didn't know there was a problem and as such you didn't run the code in a debugger
- ProfXponent 4y agoI agree that logging is essential in any system, but I read most of the original comments as tossing console.log(foo) at various points to manually observe the outcome. The approach you described is much more structured and absolutely vital.
- Joe_Boogz 4y agoIt’s relatively easy to use logging in an async matter. One windows approach, for example, is to set a thread activityId. This allows you to track a specific operation or context across threads / time. Use with a filtering tool to filter in on a specific area of concern on specific threads. Bonus if your logging also is tracing and ingested into some big data or telemetry tool.
- davidkuennen 4y agoI do this and for me it's the most efficient way most of the time.
- mseepgood 4y agoSo? Use the tool that works best for you.
- bayonetz 4y agoIn this thread, people making all sorts of claims about print() debugging being better because of speed, efficiency, less overheard, “debuggers are too hard”, etc. on and on, but not actually providing evidence for their claim. For example, how do you know it’s faster for you? Less overheard by what comparison? Like did you give a debugger an equal and fair shot for some appreciable amount of time? I routinely probe these type of assertions from my coworkers about print()’s superiority and find that ~75% of the time, they’ve never given the debugger a fair trial or invested in understanding all it’s strengths and capabilities. ~25% of the time they’ve done a fair comparison and print() debugging is indeed the right choice for their context. Fine. Good for them! Every now and then someone takes me up on teaching them how to use a debugger effectively though and they are almost without exception, albeit begrudgingly, astounded at what they’ve been missing out on only using print().
- rTX5CMRXIfFG 4y agoI know for a fact that print() is faster than breakpoints because debuggers take so much longer to pause and capture the heap and the stack at the breakpoints, whereas printing (or even logging) is just another line of code to run without interruption. Also, sequential console logs are much easier to comprehend than breakpoints when you’re debugging concurrent events.
- Beltiras 4y agoI use Sentry in development and find it indispensable. I'll also use the breakpoint embed from ipython to get a shell prompt from a hotspot of trouble. But I most often use prints. They are just too easy and 95% of the time expose what I need to know to fix the problem.
- kjeetgill 4y agoThe problem with using debuggers is it's weakness: debuggers don't scale — they're very human centric, interactive and people are slow. If I can produce a printf() log/trace + grep, picking out needles in concurrent haystacks is incredibly efficient. 1 in 1000 events are very detectable. Breakpoints would wedge, possibly crash, and frequently mask/alter execution. And I say this having been "the guy who can debug anything" at a few places using debuggers, having written programmatic debuggers, and built all sorts of contraptions to debug issues as needed.
- lukego 4y agoThese days I debug by doing "data science" on print statements.
- oumua_don17 4y agoI revert to print statements except when using Slime debugger. Lisp live debugging is underrated.
- bee_rider 4y ago"What else is there to use? The print *, 'var_name:', var_name statement is marshaled up perfectly by pretty much every MPI runtime. There just doesn't exist a user-friendly MPI debugger," he said cunningham's-law-ily.
- conradludgate 4y agoIn my case, I tend to leave debug level logs around my code. They are statically disabled in deploys so I can just turn on debug logs locally to see. One thing I've noticed doing both .NET and Rust at work is that I often do use my Jetbrains debugger for C# but rarely do I use gdb or lldb for my Rust. Rust feels a lot easier to stick a few more debug logs than it is to use the debugger. C# being VM based means it has a first class debugger that is invaluable to the language (I often joke that our C# code follows Debug Driven Development)
- bawolff 4y agoWow the pro debugger people here are really defensive, saying that the only reason people wouldn't use a debugger is incompetence. Sheesh, get over yourself. You use what works for you, i will use what works for me. Which is sometimes a print and sometimes a debugger depending on circumstance.
- invalidname 4y agoLots of times it's just ignorance. People don't know about basic things like tracepoints which are like log statements. But you don't need to clean your code after them. I work in that field so nonsense like that does piss me off. Yes I used logs in embedded and other cases where debuggers weren't an option. But you don't tweet about those cases. This is clearly a stupid thing to say in this case.
- dilyevsky 4y agoAnecdotally i found engineers who lean heavily on debuggers are actually the ones who take forever to ship anything (ofc there are exceptions) but maybe it’s just me.
- zh3 4y agoIf building and running takes a second, printf is great (especially with inotifywait - as soon as a file is saved in the editor, trigger a build and run in a separate window). Even on programs approaching 100K LoC, ccache and make build the whole thing in less than a second. On the other hand, if build/run is a 15-minute cycle, a debugger makes a lot more sense; also for things like apparently random crashes that leave a coredump.
- WalterBright 4y agoI use debuggers usually for the stack trace. I use print's for most everything else. The reason is pretty simple - the print's customize the dump for the specific problem I'm trying to track down. Using loggers generated far, far, far too much noise and not enough signal. When the problem is found and fixed, I use `git diff` to find all the added debug logic, and delete it before checking in.
- diarrhea 4y agoSince branches and such are cheap, you might benefit from `git switch -c debug`, `git reset` etc. They can wipe your working tree clean instead of having to do it manually.
- WalterBright 4y agoI didn't know about that switch! thanks
- gugagore 4y agoThere are some cases where the smallest unit that the -p(atch) flag considers is not granular enough. In those cases, you can resort to manual editing. Even with that, I find it to be a huge timesaver.
- diarrhea 4y agoI suspect you already implicitly included it, but just to mention it: the hunks created by `--patch` commands can be split up further through `s`. But as you said, this is sometimes not enough either (two changed, adjacent lines will remain atomic).
- rhizome31 4y agoI don't know, in practice you'll also have useful changes that you want to keep. I usually resort to `git checkout -p` to get rid of the debugging statements.
- 4y ago
- WalterBright 4y agoIn the early daze I was debugging the software I wrote for an embedded system. I wanted to make sure the interrupt service routine would run fast enough that cascaded interrupts didn't happen. The easiest way was to simply attach a wire to the interrupt pin in the CPU running to a speaker. The resultant buzzing was all the info I needed. (Human ears are very good at detecting patterns.)
- alfiedotwtf 4y agoRelevant: https://news.ycombinator.com/item?id=28251896 https://news.ycombinator.com/item?id=28251896
- gorgoiler 4y agoThe vast majority of developers still debug by looking at, and then editing their source code. Python matches up with this style perfectly. The next time you go into insert mode and feel like adding print(x), add a call to breakpoint() instead. https://docs.python.org/3/library/functions.html#breakpoint https://docs.python.org/3/library/functions.html#breakpoint `debugger;` does the same thing with browser JS engines.
- IYasha 4y agoI do. It is by far the most portable way of doing things, if not the most reliable (but it may also slow things a lot). When someone asks me to help debugging in unknown language, on unknown platform, the first thing I ask is: "how do I printf here". Most of the problems are traceable this way. It is VERY rarely a low-level (reg/stack/mem/system) fault.
- rwmj 4y agoIt's not helped by gdb and DWARF debuginfo being objectively terrible. There should be no circumstance where the value of some variable has been optimized out. That happens so often that to use gdb I have to recompile with optimization off, so I might as well add print statements. The CLI of gdb is also awful, with common operations like printing arrays or memory being hard and obscure. There's a TUI, if you remember the key to get into it, but you wouldn't know about it because despite the verbosity of gdb start up, it doesn't tell you about it! And the manual is an info file, possibly the worst documentation format invented.
- roca 4y agoThat's all fair, but the blame also falls on compilers, especially clang/LLVM which do a fairly poor job of tracking variable locations through optimizations.
- knome 4y ago>There should be no circumstance where the value of some variable has been optimized out. That's what optimization does. If you have `x := 1 ; y := 2 ; z := x + y ; print z ;` optimizing will just leave you `print 3` in most languages. There just won't be x, y or z values after the optimizations are done. If a value is to be calculated then passed down some stack of functions, but during optimization the value is folded into a constant, then the functions rewritten to use the determined constant and then the now unused parameters are dropped, what is it supposed to do? Pretend the value is still there? Faking stack frame details seems particularly counter to what I want a debugger to be doing while I step through code. Leaving the variables in place would slow and bloat the generated code. >common operations like printing arrays or memory being hard and obscure obscure, perhaps. I did have to look up array printing syntax, as I usually only print variables or specific array entries. It's not hard that I see, however. $ gdb a.out (gdb) start hello world this is a test Temporary breakpoint 1 at 0x4004da Starting program: /some/path/a.out hello world this is a test Temporary breakpoint 1, 0x00000000004004da in main () (gdb) step Single stepping until exit from function main, which has no line number information. __libc_start_main (main=0x4004d6 <main>, argc=7, argv=0x7fffffffdd58, init=<optimized out>, fini=<optimized out>, rtld_fini= <optimized out>, stack_end=0x7fffffffdd48) at ../csu/libc-start.c:325 325 ../csu/libc-start.c: No such file or directory. (gdb) print argc $1 = 7 (gdb) print *argv@argc $2 = {0x7fffffffe0f0 "/some/path/a.out", 0x7fffffffe10b "hello", 0x7fffffffe111 "world", 0x7fffffffe117 "this", 0x7fffffffe11c "is", 0x7fffffffe11f "a", 0x7fffffffe121 "test"} (gdb)
- kabdib 4y agoIf you are designing or implementing a language, do the debugger first. Well, very early, anyway. Think of this as deep introspection into how well your design holds together. If you can't write an effective debugger for the thing you're hatching (or at least, an API and some good tests), then maybe you're looking at a design flaw. And in any event, you'll have a debugger when you need one. And you will need one. This will probably speed up your development, and your users will hate you less.
- mbrodersen 4y agoI happens to be a professional compiler developer (among other things). And nope that is not the right answer. The right answer is 1k tests making sure that a new version is (almost) never released with bugs. I haven’t had a bug in production for 5+ years. And routinely completely rewrite and optimised parts of the system with no fear.
- kabdib 4y agoI think we can agree that if you don't have tests, you don't have a compiler. You don't have anything, really not even a toy. But there are too many languages out there in production that don't have debugging support worth a tinker's damn. (I spent quite a few years working on toolchains, jitters and runtime systems. It's fun stuff).
- bluenose69 4y agoAlthough I find breakpoints to be helpful in some simple cases, for complicated work I prefer the print() approach because I can do complicated calculations before deciding whether to print (and what to print). Plus, saving the information in a file is very handy, because it lets me examine state in some detail, with split editor windows, grep commands, etc. Quite often I'll preface the print() output with special tags that I can use to sift through the log in an editor or, in more complicated cases, with code that analyses the condition numerically or graphically. I've been doing this for several decades. It ain't fancy, but it works. Compared with debugger actions like breakpoints, this helps me solve my problems much more quickly, and with greater insight. Reframing Graham's statement at an individual level, print() is what I use, the vast majority of the time.
- dan-robertson 4y agoYou can also just use conditional breakpoints or add code to do some test and then call some dummy function on which you set a breakpoint. I don’t want to suggest that tracing or whatever the system you describe isn’t useful, but I think the main advantage people get from a debugger is being able to look at the values of things and step through the program execution (and often find that it does some surprising things).
- someweirdperson 4y agoDifferent problems, different strategies, different tools. Does it crash? Debugger, backwards from the crash. Is there some "wtf is going on 'here'" with a pretty undefined 'here', and only sometimes under unidentified conditions, but likely algorithm-related? logs, assertions, printfs. Until some more clear idea of the source of the problem manifests. Does the former turn out to be truely bad, in the sense of unintended side effects e.g. by something writing beyond valid addresses and it is known what is being overwritten? Debugger. Finally, is there some misbehavior in some already identified range of code? Debugger. Of course the environment matters, too. Build time vs. debugger features available. Most effort/time is needed to solve wtf-style problems. As such, a significant part of the time/effort can plausibly spent for some form of printing. The last kind of problem (bad part of the code already known) doesn't usually need a debugger: If the faulty part is already narrowed down, it is easy to spot the problem with eyes in most cases (at least knowing the intended behavior, maybe not when looking at some random unknown code). Summary: More problems can be solved using a debugger quickly, but more time is needed to trace down those problems that cannot.
- DangitBobby 4y agoCan debuggers go backward?? If I had access to that, I would probably use it a lot more... I don't think there are any Python debuggers with that capability.
- schwede 4y agoIn Visual Studio if you drag the execution arrow up, it will rewind to that point. It works really well. It’s okay especially useful when you step over a line you wanted to step into instead.
- ChrisMarshallNY 4y agoFor my server work, I almost exclusively use print(). That’s mainly because I don’t spend much time there, and it’s not worth it for me to spend the time setting up xdebug, or whatever. It’s worked fine. I’ve written some pretty huge systems, that way. For my client (native Apple) work, I use breakpoints all the time, but they are not always that useful; especially when working in a threaded/time-critical environment. I often add print() statements, to display aggregate and calculation results. Swift has a fairly nice reflection system, that can be used to rabbithole a lot of information. When running in debug mode, my console spews out all kinds of stuff. It can be a firehose, so it works best, coupled with breakpoints. Been debugging for over 35 years. I’ve learned use whatever tools are available, but my most useful tool is in my noggin. Those of us "of a certain age," that have done firmware development, may remember ICEs (In-Circuit Emulators). These would probably be impossible to do, these days, but they were about the best debugging tool you could ever hope to have.
- mtlmtlmtlmtl 4y agoI've been working on a chess engine as a side project. Often, using print statements is the best way to chase down certain bugs where using a debugger isn't particularly feasible. E.g if there's some bug in my search function resulting in weak play, but it only shows up at higher search depths for some reason. The search function is recursive, and the number of nodes visited grows approximately with sqrt(b^d) in the best case with ideal pruning. b is the branching factor(in chess this is ~30) and d is the search depth. That's billions of stack frames to look at. The only way I've found of debugging these is through print statements and grep/awk. As well as programming it to generate some statistics that may or may not be helpful. I've yet to find much use from debuggers.
- roca 4y agoOld-school interactive debugging tools like gdb and Visual Studio are pretty bad. But new-school debuggers with record-and-replay and features built on top of that --- e.g. rr-project.org, undo.io, replay.io and (plug) pernos.co --- remove a lot of the downsides of "stop and step" debuggers and add a lot of extremely useful features on top. E.g. in Pernosco you can capture most of the benefits of logging by querying for the executions of a particular line, filtering on a conditional expression, and rendering the values of chosen expressions at that line: https://pernos.co/about/expressions/ https://pernos.co/about/expressions/ ... except that you get those results without having to recompile or even rerun your program. And when you find the event you're looking for, you have instant access to the complete program state with the full power of the debugger. replay.io has something similar.
- chronophylos 4y agoI don't know how to debug async code in Rust without print-statements / log entries. If I step through the code it steps through the polls. Not to mention everything breaking if you need to talk to a server.
- samsquire 4y agoI don't think debuggers are superior than print statements or Jepsen. I work with multithreaded code and lock free data structures. I don't use a debugger. So I rely on having a model in my head that could be wrong and what I plan to do is analyse logs after a test run. These are guaranteed interleavings that actually ran. When you have 100 of threads and random interleavings your mental model is problem is necessarily different to thinking I'll reach to the debugger to tell me what the program is doing. When you want serializability you need to design your system differently. I implemented multiversion concurrency control and use timestamps for correctness. I have yet to write a TLA model for it though. https://github.com/samsquire/multiversion-concurrency-control/issues/4 https://github.com/samsquire/multiversion-concurrency-contro... I also want to test my Raft implementation which isn't properly threaded. But I also need to test pathological cases such as old raft nodes coming back. Most of the time you're reaching for the debugger is that you don't understand why your web framework got into such a state where something is null such as your Inversion of control Java container as the control flow is so complicated. It doesn't help with multithreaded programming.
- Agingcoder 4y agoThe 'design system differently' is a very good point, especially for distributed systems. Basically you have to build correctness and debuggability into the development process, and these are actually difficult problems on their own for these kind of systems.
- samsquire 4y agoI try to understand the reasoning of a concurrent system If two threads (or computers) can simultaneously be at any point in execution, you need to lay down some baselines for future execution. If the baseline is not met you need to Thread.yield or busy wait or busy wait and then park yourself for scheduling by the OS. So you need to wait until the system state is valid to modify - there is nobody else modifying state. I implemented the left right concurrency pattern but it causes duplication of data. One of my ideas is to combine it with arrays of structures and structures of arrays to at least benefit from the duplication of storage for parallelism. Once you got past that check, you do your work and then compare and swap and check that the world is still valid - there might have been someone else who was faster than you. I'm not an expert I am just interested in multithreading and scalability challenges. If there is multiple changes, such as a doubly linked list. I think you need multiple CAS and you need to update your guard to guard against that partial update too. How do you avoid a partial update being read? You could offload the check to a reader of the linked list to check if there is a partial update on that node. These problems are difficult to debug but you need accurate thread interleavings that you can sort into total order which is difficult as some things are going on at identical times. I've been reading Go scheduler sourcecode go/proc.go trying to understand how it multiplexes G goroutines onto threads M. Lots of locking going on! I am interested in bloom Lang too for distributed programming.
- Ambolia 4y ago"prints" give you an immediate picture of the whole execution, if you place them properly. A debugger leaves you isolated in one specific part of the code and you have to crawl out of there with navigation tools that usually are not very good. The only time I find debuggers useful is if you can tell the issue from the current value of the variables, or if the issue has already been isolated to something like a tight loop of 10-20 lines that are doing a tricky operation.
- mkl95 4y agoI used to do that until I learned to stop worrying and love the IDE. I still use Emacs for a few tasks Jetbrains IDEs are not good at.
- CSDude 4y agoThe debugger on IntelliJ is great, can do conditional breakpoints and calculate expressions on the fly, save previous expressions. That’s all I need from a debugger and mostly complements my print statements.
- brunojppb 4y agoYes, and that is fine.
- cool-RR 4y agoShameless plug: PySnooper is a debugging tool for Python that lets you debug in a way that's as easy as adding print statements, but gives you a lot more information automatically. https://github.com/cool-RR/PySnooper/ https://github.com/cool-RR/PySnooper/ HN thread: https://news.ycombinator.com/item?id=19717786 https://news.ycombinator.com/item?id=19717786
- slim 4y agoI think debuggers are useful when your edit code -> print result cycle is longer than a few seconds. When you need to compile for example.
- srazzaque 4y agoI don't quite understand the argument here. Logging statements and "using a debugger" are not enemies of one another, with tribes of people in one camp versus the other. Logging, done well, will inform (1) exactly what version of your code you were running 3 months and 12 days ago, and (2) where the potential issue may be. Therefore it will inform you _where to place a breakpoint_ should you wish to repro the issue and debug locally. Good luck getting started in a local debug session without having had adequate logging in place.
- deepGem 4y agoEven better luck debugging in the enterprise without logging. You won’t even know which machine your code is running on and in which machine Splunk is spitting out the logs. Without printing time stamps and debug worthy information to the logs, you are fucked, period.
- lytefm 4y agoAlso, when trying to reproduce an issue, adding logs can often be more helpful than using a debugger: With the debugger, you can only see the application state at one specific point at the time. With added print statements, you get a nice history of the application state. If I already know where exactly something is going wrong: sure, I'll could a breakpoint there, if needed - often it's not. If I don't know where exactly the problem is, I'll add print statements.
- DougBTX 4y ago> With the debugger, you can only see the application state at one specific point at the time. Really depends on the debugger. Some tools support “time travel” debugging, which essentially stores a structured log of memory state over time.
- Qem 4y agoThe Smalltalks are nearly unmatched on this. Pharo is a great representative of this tradition.
- brailsafe 4y agoIt's not that difficult to setup a debugger in most cases, but in javascript just write the word debugger wherever you want it to stop, and then open devtools. print statements can come in handy, but maybe once a year.
- dugmartin 4y agoEase of debugging for me peaked in the mid-90s when I did embedded system development using a 486 bond out emulator for GE. We had a couple in the lab (I think they cost $40,000 or so each) and they enabled breakpoints on really low level changes to the processor, down to changes to bus lines. Iirc it also allowed looking back in time on bus signals and memory values.
- jiggawatts 4y agoI just had a conversation with a dev manager about how the vast majority of dev teams I've encountered use maybe 5% of the tooling available to them. Debugging using just debug tools in the IDE is just scratching the surface. A demo I like to do is Azure Application Insights, which takes memory snapshots of web server processes in production whenever they throw exceptions. Combined with source indexing, it lets you "debug" a release build from a month ago in a matter of seconds. Just recently I was trying to do the sales pitch to a semi-technical product manager, and he mentioned that they were seeing "occasional login issues". While on a video conference session we managed to repo the issue: after entering a valid password, the application promptly reloaded the login prompt without any error message. Sit down and think about how long you would estimate a bug like this would take you to fix: "Occurs in prod only, infrequent, no error message, just does nothing." App Insights helpfully had a "debug snapshot" available for the error. It was literally a single click to load it in Visual Studio. It jumped straight to the line of code that crashed, where there was a comment along the lines of "we may be too lax in parsing this". The snapshot let me inspect the variables and... yes.. a bit too lax they were. I did this in about two minutes while chatting with the manager. Boom. Solved! I've had similar troubleshooting sessions in the distant past that took weeks or even months to resolve, because I just didn't have the tools available to me. Tools make all the difference, and anyone who argues against this just doesn't "get it" in my opinion. I don't care how wizard you are at shovelling, you aren't going to out-perform the back hoe.
- triter_than 4y agoIs this just a pitch for Azure Application Insights, or do you have other example backhoes? (If it’s just a pitch, it’s a great one! I just don’t use Azure currently).
- jiggawatts 4y agoI don't work for Microsoft and I'm not selling anything. The point is general. Any time you hear terms like "time travel debugging", you should get excited, because someone out there has worked very hard to make your life easy. It's just ridiculous how much effort has gone into some of these tools, and then has been ignored. It's a waste that bring tears to my eyes. You have all been given light sabers, and you hit each other with blunt sticks.
- marginalia_nu 4y agoIn the majority of cases, printf debugging is faster than debugger debugging. Debuggers can definitely help with some more gnarly bugs, but those bugs are rare. Experienced developers aren't marked by debugging one way or the other, but using the appropriate tool in the appropriate situation. I also think printf debugging is one of those first to learn last to master sort of methods, as their utility is closely tied to the accuracy of your mental model of the code.
- Morgawr 4y agoI work with a lot of different platforms/languages/programs, I've debugged code in various parts of the stack (kernel, firmware, virtualization, drivers, hardware devices in the lab, serial-connected devices, etc). Sometimes I just want to see if the code I am deploying is hitting the right areas, if I am entering the right function, if a certain variable is a certain value that I expect or why it is crashing. I don't have time to learn the specifics of each language I use with each specific stack implementation that I'm working on (C for kernel and drivers, rust for virtualization infrastructure, C++ for some services, bash/sh for some startup scripts and glue code, python for deployment scripts and tooling, etc). I just want to be able to print something, have it show up in the logs, and take it from there. Print debugging just works fine and it's a great aid, and I've seen most of my coworkers make use of it. Debuggers are great for more domain specific usages, people who work on a very specific project or service or application should definitely set up debuggers, but in my day to day work I probably switch between something like 10+ different projects weekly. Most of those also operate in such a distributed/remote manner that it's simply not possible to (easily at least) install a debugger with breakpoints and instrumentation. I've done my fair share of gdb debugging for performance profiling, and that's fine, but that's also a much more involved process that is not as simple as simply writing print("we got here!") all over the code :)
- nomilk 4y agoRuby and R and both have amazing interactive debuggers: binding.pry and browser() respectively, and I use them all the time (never print statements). So I don't understand the significance of PG's tweet; do other languages have less-good debuggers, and hence people get stuck using print statements? Or have I missed his point entirely?
- devoutsalsa 4y agoI often use… 1/0 …or… raise “potato”
- FatalLogic 4y agoA 'Print 1/0' is the People's Breakpoint (it may also be the best choice if you're debugging a language that you hardly know, so you just want to throw an error and halt)
- Fiahil 4y agoYes, because I didn't find a way to attach a debugger on Databricks.
- CGamesPlay 4y agoPrintf debugging is to programming what spreadsheets are to analytics. Sure, you can make a better tool for some of the jobs, but it’s pretty much impossible to make a tool that is better against every facet of the incumbent.
- FartyMcFarter 4y agoDebugging is largely an effort to understand what's going on inside a program's execution. Having the problem talk back to the programmer via prints/logging is a perfectly sensible way to do this. I have successfully used prints to fix tricky issues, in my opinion it is pure snobbery to deride this approach.
- ay 4y agoWell placed and performant logging can be absolutely crucial: - for a simple bug, it can allow to understand the root cause and fix the problem before even reproducing it - for a tricky bug, it can help understand how to reproduce it, and verify that you are reproducing the correct one - and then comfortably explore it in a debugger. Source: I had been troubleshooting both kinds of the above in networking gear for a decade as my professional occupation.
- kentonv 4y agoIf you have a fast, repeatable automated test that triggers the behavior you want to debug, then just about everything a debugger can do can be accomplished more easily by injecting code and re-running the test.
- gnulinux 4y agoI am a proud print debugger. I think the key is to be sure you know how to debug using a debugger and know situations where that'll be more efficient. If the trade-off is small, I think sticking to your preferred method is ok. I write tons of fast unittests, so that I can get fast feedback from the system if anything unexpected happens. I think print debugging works quite well for this setup.
- jasfi 4y agoOne of the great things about print statements is that you can easily copy and paste them. Also searchable, as another poster has already commented on.
- eternityforest 4y agoA language without excellent debugging is not a language I want to use at all. Print statements are sometimes needed for embedded, but that's probably my least favorite part of embedded.
- mbrodersen 4y ago25+ years of professional experience here. I very rarely have to debug (because of extensive test suites). But when I do, I always use printf. It gives me the full power of the programming language to selectively detect and print the key information I need to reason about the system. It works great. I know how to use debuggers. I just choose not to.
- chucklenorris 4y agoI don't think anyone is arguing that print debugging isn't useful or is somehow archaic. That's a straw man. What the author and I experienced is that the vast majority of developers don't use an incredibly powerful tool when it's available, and relatively easy to set up. I personally saw developers go through compile time cycles of 30 minutes to add and remove print statements than to bother using a debugger. Most people don't understand what's a stack view, or memory dump or the fact that you can modify variables at runtime to skip conditional checks to avoid recompiling. On dynamic languages it's even better, you can easily evaluate code, replace functions, import modules and call other unrelated functions, etc. That saved my bacon noumerous times in production with nodejs and python. Just connect to the container, send sigusr1 to the process to open the debug port, forward that to my machine and then connect my debugger and run code/json dump large data/put a conditional breakpoint and wait for that hard to reproduce bug to come to me. This kind of silly stuff got me "wizard" status in orgs i've worked in over the years and substantial pay raises over devs that were more conscious/hard working than me.