Hacker News new | ask | show | jobs
by bakatubas 1888 days ago
As a counter-point, I think there’s an argument that folks don’t spend enough time in the debugger. But there’s a lot of value there and in fact one could use a debugger environment to unit test as even native debuggers have scripting environments.

Personally, I think folks should master the debugger _first_ and during all steps learning a programming language.

But similar to test-driven-development it’s a different way of thinking, and most books scarcely discuss the debuggers.

That being said, I do use print-debugging a lot too—in C++ a lot of functionality can be compiled-out, allowing one to, for instance, print hex dumps of serialized data going to the network.

On that note, there is a distinction between trace debugging that is part of the source code and general print statements that are hacked in and removed.

3 comments

I can believe there is value in learning a debugger, but debuggers could stand to improve significantly. Debugger UX is almost universally awful and per the parent it’s often difficult to get one up and running. Moreover, if you do your “print debugging” with log statements of the appropriate level, they can be useful in production which is perhaps the biggest value.
Also, in almost all languages debuggers are an afterthought. Take e.g. the situation with Golang, Haskell or Python. Either there is no useful debugger or there is one, but it came late and still cannot debug everything the language does.
Print debugging not (really) working is haskell is... Non-idea, and a bad pairing for bad real debugging. But test cases are usually easier to figure out. Presumably there's a balance discovered by people in big projects but it never seemed as good as normal approaches to me.
Haskell debugging by testing is great for small functions where you can use quickcheck. But larger tests for the more complicated stuff don't work in quickcheck and there isn't much else that one can easily do.
Not sure what you mean, there's e.g. Tasty for non-QC testing. It can do all sorts of variations of test, e.g. traditional unit tests, "golden" tests, etc.
I haven't actually used quickcheck in Haskell, but I've used it for very complicated tests in other languages including Racket, TypeScript, Rust, and Java. The nicest thing about quickcheck is that it lets you easily create test data without imposing too many constraints on it. Regular fuzzing or randomized testing is almost as good, but the narrowing done by quickcheck is sometimes nice for understanding test failures.
On the other hand, it is reall great in case of the JVM
Not sure what situation you are talking about. Debugging Python is as easy as right-clicking a file in Pycharm and pressing debug. Why care if it was an afterthought when it for the past decade has worked perfectly.
I care. That it has worked for the last decade only means that Python was without a working debugger for 2/3 of its existence. 1/3 of which I had to suffer from it.

Also, pycharm isn't really what I would call a proper debugger yet, attaching remote running processes for example just doesn't work reliably yet and is very new anyways. Debugging embedded targets just doesn't work. Multithreading is iffy (but that's unfortunately normal in Python).

Also needing to use a particular text editor to use a decent debugger is bananas.
The problem is that it's not just a matter of "learning the debugger for Java." In practice there are many different projects that configure debugging many different ways, and it doesn't matter that you know which keys to press in IntelliJ if it will take you an hour to figure out how to attach it to the project. This speaks to OP's point, where it's hard to use a real debugger to casually investigate random projects.

Having said that, it is absolutely a requirement when working on a project for any length of time (especially professionally) to set up and figure out a debugging environment, because it is significantly more productive than printing. But the startup cost is certainly there.

The java case is actually pretty universal ... you run the JVM with debugging enabled (fixed string of flags) and then tell your IDE to attach to the JVM on the port you gave it. You don't need compileable source, can be on a remote server, different OS etc - if you have just the source for the bit you want to debug you can set a breakpoint in it and it'll stop there.

Being able to debug third party code in remote / hostile environments (even when its mixed with proprietary vendor code) is one of the things I like about Java.

The Java case is arguably the least difficult out there thanks to reasons you outline. But still, the other day I had to debug a Gradle plugin written in Java. It's possible! But it took an hour or so of effort to figure out which options to use and which program to give them to.
> The problem is that it's not just a matter of "learning the debugger for Java."

In the Java case, for stanalone projects (i.e. not something deployed on a server) an if it is your own project and you don't do anything unreasonable it is mostly just set a breakpoint and hit "run with debugging".

Probably the least painful debugging experience I know.

Doing it for Tomcat/Tomee was slightly more advanced IMO but still utterly trivial compared to wrangling css or js ;-)

There are reasons why we "old folks" like Java so much despite its verboseness.

In a world with perfect optimizing compilers that never introduce bugs, we should never "need" print debugging. But that's not where I live, so I'll keep using print debugging.

On the other hand... adding print statements can also invalidate certain optimizations (an excellent source of heisenbugs), so I'll never stop using debuggers either

Print debugging is essential in distributed systems. Sitting in the debugger waiting for human input often leads to timeouts and not covering the case you want. Of course, sometimes adding the prints, or even just collecting values to be printed later also changes execution flow, but like do the best you can.
All the more reason for folks to spend more time using the debugger; for instance, break points are only one feature.

Setting up remote debugging I’ll agree is more difficult than a local application, but each remote machine can automatically run startup commands and not require user input; commands can be run at particular places too (to print output etc) with conditional trace points, all while not impacting the code itself.

Main point is that folks don’t spend enough time learning the debugger, as print statements are easier. But using the debugger is a better practice in my opinion in the case where print statements are added just for a quick test, then removed.