Live data from Hacker News

Why debugging is all about understanding

futurice.com

51–60 of 80 posts

Re: Why debugging is all about understanding

#51
post #48
post #12

Over the years I've become more and more convinced that debugging has done a lot to develop my problem solving skills and analytical thinking in general. It's like being a full-time modern times Sherlock Holmes with the crime scene and tools neatly at your disposal. Usually breakpoints and stepping through code solves things pretty easily but some bugs can be real stumpers. Here's some of the tools I tend to use: 1.…

I am sorry, but it is contrproductive. It is better to invest time into better code, which will find and tell you about problem, because such code can be reused many times by whole team. Instead of playing Sherlock role, think: why your code allowed you to make that bug? Maybe you need to improve your code, add more test cases (or improve existing), add beter comments, add more examples, add more flexibility in confi…

Sometimes that is true; if your architecture is suboptimal then yes you can trim away huge classes of bugs with a high-level change. But as with all aspects of software development, the truth is a question of nuance and judgement.

The way your assertion can go wrong is when you don't recognize the errors that your rewritten code will add. At a high level we always envisage perfect code, and existing code is always seemingly full of warts, but no perfect coding plan ever survives contact with reality. Those warts are bug fixes and optimizations which make the thing work. Don't be too quick to rewrite lest you create bigger problems than you started with.

Re: Why debugging is all about understanding

#52
post #3

+1 for encouraging the use of logs as a debugging tool - they're often better than a debugger in my opinion [1]. Also, bugs aren't only bad - you learn a lot from them too [2]. [1] http://henrikwarne.com/2014/01/01/finding-bugs-debugger-vers... [2] http://henrikwarne.com/2012/10/21/4-reasons-why-bugs-are-goo...

Thanks for linking to your blog. I enjoyed your writing and look forward to following it.

Re: Why debugging is all about understanding

#53

Earlier quoted context omitted.

Binary search is not random permutation. The hypothesis is "problem lies in this half of the codebase". Hey, if your test passes after commenting out some stuff, great.

And if that hypothesis is based on data, great. And if there is no other alternative, fine -- but this technique should be viewed as a last resort (and an unusual one), not a first step.

Wait a minute. First of all, the data is simply "there is a reproducible bug in the program". The real hypothesis is that a small, localized portion of the code is responsible for the defect. To find it, you can try binary search. If you have a better idea of the location, then by all means.

Second, when you have a bug and you write a unit test, you are effectively commenting out the entire codebase except for the function under test. When you have a compiler error, whether it's a syntax/semantics bug in your code or a bug in the compiler, sometimes you need to produce a minimal example, so you have to cut cut cut until the bug is just barely provoked. When you have a pipeline of data transforms, and the end result is suddenly borked, it can work to chop off half the transforms and look at the result. When latex is crashing for some unintelligible reason, just comment out half of your document and see if the problem goes away.

Sure it's really dumb if you're just excluding a* .c through m* .c (spaces due to HN formatting rules), but figuring out if the problem is in the first or second half of main is not outrageous. I don't think the guy was presenting it as the first step ("If you have no idea where your bug lives"), but I do agree that it comes across as a little naive, since he should have talked about all of the other techniques available first. So the problem isn't so much the lack of a hypothesis, but the inefficient experimental approach of using a brute force technique indiscriminately.

I think the last resort is reached a little sooner for some types of bugs and some experience levels (language, environment, codebase, programming), and yes in many cases it won't even do anything for you.

Personally I always liked dtrace. This guy gave a demo of it at my university once, I thought it was great, one of the best talks I've seen.

Re: Why debugging is all about understanding

#54
post #36

Earlier quoted context omitted.

Those should have been 0 and 0.1 in his list. Agree with all 12 points. My little grain of sand: use all the tools available to avoid bugs: a good IDE and/or code linters/inspectors can work wonders on a codebase. I've seen people still programming with "just a text editor" (not Sublime, vi, emacs or any powerfull text editor, I'm talking little less than Notepad with syntax highlighting) telling you "it's just I don…

I'd even dare to say that it is better to shove Vi/Emacs experience into a real IDE rather than the other way around. I really like Vim, I use it for almost everything, however when I am working on a large C++ codebase I tend to use an IDE that has full support for visual debugging, code navigation and so on. I am always amazed when some more experienced programmers than me spend valuable time grepping files and usin…

> I am always amazed when some more experienced programmers than me spend valuable time grepping files and using GDB on the command line.

I don't know about Vim, but Emacs works very well as a visual debugger, for quite a couple of languages. It looks like this for C, for example: http://www.inet.net.nz/~nickrob/gdb-ui.png (it's Emacs debugging itself in this screenshot).

In general both Vim and Emacs can become IDEs that are on par with other offerings. For Python development, I worked with Komodo, PyCharm, Vim and Emacs. With jedi[1] both editors get context sensitive autocompletion (for some strange reason called "IntelliSense" or something by some) and "find definition", "rename identifier" etc. With Rope, they both get nice refactoring support. With Magit/Fugitive, you get the prettiest and most functional GUI for git. With Speedbar/TagBar[2] you get classes outline. With yasnippet/vim-snipmate you get configurable, programmable snippets/templates. And so on and on - all that on top of largely superior editing model that is being developed and improved for 30 years.

I worked with Komodo, PyCharm and a couple of other IDEs, then switched to Vim and I didn't feel that I'm missing something. Then I switched to Emacs because I wanted an editor that I could easily customize as much as I'd like, and VimL just didn't cut it. There is nothing comparable to Emacs in terms of extensibility, LightTable and Atom may get there with time, but I suspect it will take quite a few years. No other IDE even tries to approach this level of extensibility and customizability.

[1] https://github.com/davidhalter/jedi [2] Vim version: https://i.imgur.com/Sf9Ls2r.png

Re: Why debugging is all about understanding

#55
post #12

Over the years I've become more and more convinced that debugging has done a lot to develop my problem solving skills and analytical thinking in general. It's like being a full-time modern times Sherlock Holmes with the crime scene and tools neatly at your disposal. Usually breakpoints and stepping through code solves things pretty easily but some bugs can be real stumpers. Here's some of the tools I tend to use: 1.…

Regarding 7, I think you should explicitly mention "git bisect" and similar tools. (Or, are you doing that step by hand?!)

For me, when I run out of ideas, "git bisect" is the most efficient debugging tool. On success, it allows me to pin down the exact commit that introduced the bug. It is very fast to do (binary search), and on success it narrows the bug down to a relatively small amount code to check.

Of course, this only works if you have a clean code history: small commits and always a working state after each commit. That is, no intermediate crap commits, and no monster commits doing 3 things at once.

Re: Why debugging is all about understanding

#56

Earlier quoted context omitted.

I agree, understanding certainly can't be emphasised enough - I believe that a good programmer must always be able to understand enough to "mentally execute" - manually stepping through code, possibly with pencil and paper or a whiteboard, and making sense of the results at each step. There are some who argue against this, and their argument is effectively "if the machine can do it for you, why should you need to kno…

But how can I mentally execute code if the whole point of the code is to interact with a third-party OS facility or application that's closed-source and therefore opaque? In that case, my mental execution of the code will depend on a mental model of the third-party component that's probably incomplete. Might as well just let the machine run the real thing.

> In that case, my mental execution of the code will depend on a mental model of the third-party component that's probably incomplete.

That's the whole reason why you would want execute the code mentally. If you don't execute the code mentally, you might not realize that your mental model of the third-party component was incomplete to begin with. Mentally executing code is a great way to test your own assumptions.

Re: Why debugging is all about understanding

#57
post #48

Earlier quoted context omitted.

I am sorry, but it is contrproductive. It is better to invest time into better code, which will find and tell you about problem, because such code can be reused many times by whole team. Instead of playing Sherlock role, think: why your code allowed you to make that bug? Maybe you need to improve your code, add more test cases (or improve existing), add beter comments, add more examples, add more flexibility in confi…

Sometimes that is true; if your architecture is suboptimal then yes you can trim away huge classes of bugs with a high-level change. But as with all aspects of software development, the truth is a question of nuance and judgement. The way your assertion can go wrong is when you don't recognize the errors that your rewritten code will add. At a high level we always envisage perfect code, and existing code is always se…

IMHO, precious moment when bug is found in the code AND it is not obvious, so you need to run debugger, is perfect time to improve your code. It indicates that code is complicated, or not well covered, or poorly commented (because bug is not noticed at peer review). You are going to test this piece of code anyway, because you need to check is bug fixed, so why not to spare some time to make code better and battle-test your improvement: if you catch bug in the process, then you improved code, if not: keep going.

And no, I am not talking about asserts. Assertions are only small part of real debugger.

Typical tools I use in my own programs are: good logging and tracing, which are able to explain what happens inside program without spamming me, some test cases for critical parts and for fixed bugs. Often, I will also split large program or script into smaller independent parts, with their own options, configuration, loggind and test cases. It's all.

Re: Why debugging is all about understanding

#58

Earlier quoted context omitted.

And if that hypothesis is based on data, great. And if there is no other alternative, fine -- but this technique should be viewed as a last resort (and an unusual one), not a first step.

Wait a minute. First of all, the data is simply "there is a reproducible bug in the program". The real hypothesis is that a small, localized portion of the code is responsible for the defect. To find it, you can try binary search. If you have a better idea of the location, then by all means. Second, when you have a bug and you write a unit test, you are effectively commenting out the entire codebase except for the fu…

Part of the scientific method is that you generate hypotheses based on the data you have, not that you generate random hypotheses. It's a directed way of thinking.

> The real hypothesis is that a small, localized portion of the code is responsible for the defect. To find it, you can try binary search.

In your example, you used this as an assumption, not a hypothesis. And it's not quite right: the assumption you made is that the _presence_ of a small portion of code is responsible for the defect. That's very different than saying that the code is more broadly responsible for the defect. In my experience, very few bugs are caused by the mere presence of some code.

> When you have a compiler error, whether it's a syntax/semantics bug in your code or a bug in the compiler, sometimes you need to produce a minimal example, so you have to cut cut cut until the bug is just barely provoked. When you have a pipeline of data transforms, and the end result is suddenly borked, it can work to chop off half the transforms and look at the result. When latex is crashing for some unintelligible reason, just comment out half of your document and see if the problem goes away.

Those are fine solutions for those very specific, very simple problems. Given the problem space, the assumption that the error is caused directly by the presence of some input is well-founded.

Re: Why debugging is all about understanding

#59

While it's great to see more attention brought to debugging, some of this is just (for lack of a better word) insane. e.g.: For searching in space, most programmers have done binary-search with commented code: comment out or bypass half of your codebase. Do that recursively until you nail down where (at which file, which function, which lines) the bug lives. No, most programmers actually haven't done this for the sim…

I listened to your DockerCon talk on debugging. So in a server that can handle multiple concurrent requests (e.g. a web application server), should each unhandled exception abort the whole process (after delivering something like an HTTP 500 to the client) so the developer can do postmortem debugging on a core dump? That would cause all other in-progress requests to be aborted too, unlike simply logging the exception…

I don't mean to speak for bcantrill, but yes, this is exactly how we (at Joyent) build programs. We wrote in detail about why we do it this way: https://www.joyent.com/developers/node/design/errors

It's not that this approach motivates root-causing failures (though that's true). It's that uncaught exceptions are programmer errors, and it's tautologically impossible to correctly deal with them. Attempting to do so can make things much, much worse.

To make this concrete: I've dealt more than one production outage caused by the mere presence of an uncaughtException handler. If the program had merely crashed, a hundred requests may have been interrupted, but the program would have restarted and resumed servicing requests. Instead, the exception was thrown from a code path that had a database transaction open with database locks held. Because the uncaughtException handler just logged the exception and otherwise ignored it, that transaction stayed open (and the locks remained held) until a human got into the loop -- interfering with tens of thousands of subsequent requests. That's much, much worse. If the process had just exited, the db connection would have been closed, the transaction aborted, and the locks released.

An unexpected error handler can't know the complete state that the program was in because by definition this wasn't a state that the programmer thought about.

Re: Why debugging is all about understanding

#60
post #59

Earlier quoted context omitted.

I listened to your DockerCon talk on debugging. So in a server that can handle multiple concurrent requests (e.g. a web application server), should each unhandled exception abort the whole process (after delivering something like an HTTP 500 to the client) so the developer can do postmortem debugging on a core dump? That would cause all other in-progress requests to be aborted too, unlike simply logging the exception…

I don't mean to speak for bcantrill, but yes, this is exactly how we (at Joyent) build programs. We wrote in detail about why we do it this way: https://www.joyent.com/developers/node/design/errors It's not that this approach motivates root-causing failures (though that's true). It's that uncaught exceptions are programmer errors, and it's tautologically impossible to correctly deal with them. Attempting to do so can…

Thanks. Now I gotta figure out the best way to do that in Python. Most Python web frameworks and WSGI servers catch all exceptions and keep the process going.
Post reply on HN