Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I love using neovim but switch over to vscode for debugging. I’ve heard of vimspector and other plugins that are similar but I’m curious to hear what debug workflows do people here have with neovim/vim.

Also, do you have any plugin recommendations for debugging?



I honestly wish no one used development debuggers. If your data structures and programs are so complex you need to have deep inspection the root of that problem is your complexity, not your tooling. Every pleasurable project I've been on has been a project where print statements were the best debugging tool, because the simplicity meant I could more quickly debug something with a quick print than a slow deep dive into some debugger tooling.

Granted, I get it, you didn't design the thing but you still need to debug it so no judgement, just more of an observation that if you limit yourself to simple tooling you might be surprised at how the pain points will force you to spend more time thinking about how to design your own code to be more simple and that's bound to have a far greater impact than any debugging tool could provide you.


I've found and fixed so many bugs that are simple to deal with using a debugger but would require hours of peppering the code with print statements to even get close to a solution. Some simple examples are deadlock bugs in multithreaded programs (easy to halt the program and see what thread is blocked on what) or uncaught exceptions from somewhere unexpected (set the debugger to break on throw).

With a debugger you can also change the behavior of a live program, e.g. modify variables or jump over a piece of code that would otherwise be executed.

Every person I've encountered professionally who either "doesn't use debuggers" or thinks "print statements are better" seems to be under the mistaken impression that all a debugger is good for is stepping through code.

Debuggers are fantastic tools. You should re-evaluate your view of debuggers and you will become a better programmer.


I can imagine someone who spends time around mostly engineers that are junior enough to not know what a debuggers fundamental capabilities are might assume that anyone against debuggers is simply ignorant. I can assure you, as someone who isn't so junior that I've weighed the costs and benefits and rest firmly on the side of being against them. The benefits are obvious, but my experience has been that over the course of my career I started noticing a pattern of debugger usage and overly complex code from the people who lean on them.


I'd have to support this as well. Many folks always reel in terror when I tell them I almost never use a debugger, or I'm using gdb or lldb instead of some graphical thing if I decide that it's worth actually running.

I think they have their place as tools but largely that's dwarfed by other practices. In the notion of systems thinking, I try to limit the scenarios where needing a debugger is seen as the natural extension. This means having tests, practicing type-driven design, mapping my APIs to some real-world model (and knowing where that fails or falls short), using languages that obviate the existence of certain classes of bugs (e.g. Rust, languages with a GC), etc.

Sometimes you're trying to inspect behaviour that changes in the moment, and debuggers do great there. However, I'll say that some of the most interesting bugs I've dealt with were only visible in release builds with no debug symbols, so my debugger didn't do much of anything for me there.

Plus, this doesn't even dig into other tools that _aren't_ taught because people reach for their debuggers. How many of the folks grabbing debuggers every day have been given the same speech / sentiment given to toolz above about strace? valgrind / address sanitizer? thread sanitizer? syslog?

I feel like this comment might be a bit rambly, but I was hoping to expound on what toolz mentioned above -- there's a lot of ways to crack an egg here and the debugger is something I almost never reach for.


Using print debugging/strace/valgrind/etc, you're looking at the evolution over time of particular components of program state. Using a debugger, you're looking at all components of program state at particular time slices. They enable different viewing angles. (https://mobile.twitter.com/ManishEarth/status/13870782220560...)

I personally find that both are good tools, but to know what you should be using, you need to think about which viewing angle you want to choose: which components of state do you want to inspect and which moments in time do you want to track? If the answer is "these specific state components/unknown" (e.g. "when is my program accessing invalid memory?"), that's where print debugging comes in, with specializations depending on which particular state components you're looking at (strace or eBPF for IO, valgrind for invalid memory accesses, etc.) If it's "unknown/these specific times" (e.g. "where is my program setting HTTP response headers when a request occurs?"), then a debugger is a good idea.

However, what you said does have truth to it. Good programming practices generally center around the management of program state* (using types, specifications, whatever). If your program is not designed well in those terms, then the more likely it is that you have no idea what state components you want to inspect, meaning you reach for a debugger first. And if it is designed well, you won't need a debugger nearly as often. But sometimes we have no choice, whether that's due to an inherently hard problem space or lots of bad code that we didn't write, so a debugger is necessary.

* I don't actually know if there are programming practices that center around managing moments in time. I can't even think how that would work, but I would be very interested to know if there are any :)


> * I don't actually know if there are programming practices that center around managing moments in time. I can't even think how that would work, but I would be very interested to know if there are any :)

There was a live, 3D, peer-to-peer interactive environment back in the early 2000s called Croquet that made use of the concept of "pseudo-time." It was built on top of Squeak Smalltalk (there are some decendants today, including croquet.io). The part that handled the time management in a way you might find interesting was called TeaTime [1], and built on the ideas of David Reed thesis about pseudo-time [2]. If you are not familiar with these you might want to check them out!

[1] https://dl.acm.org/doi/10.1145/1094855.1094861

[2] http://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-20...


> But sometimes we have no choice, whether that's due to an inherently hard problem space or lots of bad code that we didn't write, so a debugger is necessary.

I don't disagree! The notion of state vs. time snapshots in terms of the mental model seems like it might be missing something small but sounds mostly on the mark? E.g. in dynamic languages my REPL is more of a debugger than any actual debugger could be.

> * I don't actually know if there are programming practices that center around managing moments in time. I can't even think how that would work, but I would be very interested to know if there are any :)

I think this depends on how you abstract control flow. Actor model comes to mind, as Smalltalk really doesn't have this notion of "time" in the same way. You debug live in such a system, and "time" is more a matter of what abstraction sent or received something (and as such may have failed). Similar arguments could be made for conditions / restarts in Lisp, perhaps even more strongly, since you can manage errors through conditions and then restart code (going back in time, so to speak) in a sort of live-debugging way. Not sure it measures quite to the degree you were asking, but that's the first thing that comes to mind.

Another thing is the movement towards async-await / cooperative coordination in programs. Even if you ignore concurrency, designing code in a way that coroutines cooperatively yield to one another (e.g. generators in Python) helps sort this out too. Basically, you make "time" in a program a function of control flow, and do that by forcing an abstraction where you explicitly yield control in the program. This relates to both the actor model and conditions / restarts in Lisp, so I feel like I'm pulling on the same train of thought there.


My recommendation is nvim-dap, the workflow is shown in my videos:

https://youtu.be/ga3Cas7vNCk

https://youtu.be/SIYt1dopfTc



I use vimspector when I need a debugger, but find I don't reach for it nearly as often as I did in vscode. Nothing against those who are effective with it but I spent too much time crawling and not enough time solving. Print lines make me think more on what I really care about.

Vimspector isn't nearly as friendly as vscode but the which-key plugin jogs my memory on the key bindings pretty fast.


I mostly work with python and drop pdb into my code if I want to step through and see what’s going on. I run tests with the make command so I can use the quickfix list to skip between errors.




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

Search: