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. What has changed? What could have caused this bug to appear?
2. Verify fundamentals: Is it actually running the code I think it does. If I change something that should change something, does it change? If I break something does it break?
3. Verify Input: Make sure it's valid
4. Verify output
5. Timing issues?
6. Slowly remove code "around" bug until it works again
7. Or start back at something you know works and add back code until it breaks.
8. Break out the functionality into an isolated environment so you can test it separately so you'll know if the problem is within the feature or a side-effect of something else
9. If your code makes it too complicated to see and find the bug refactor until it isn't.
10. Go to sleep / Go on a walk. Let your subconscious process it
and finally, lot's of bugs are made self evident by well structured and refactored code, so don't get too busy working on the symptoms rather than the root cause.
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't like IDEs" when asked about it.
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 using GDB on command line.
> 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.
While you can indeed transform Emacs/Vim to a good IDE, there are lot of (experienced) people who do not. Anecdotally I have seen quite a lot of posts boasting use of 'vanilla' editors without plugins.
Regarding your GDB screenshot, that is certainly good, I would argue though that using pure text (with some images included) is limiting oneself.
This being said, I really hope that NeoVim will go far enough and bring good enough integration of Node.js and javascript in general (my current work uses these technologies). So far I have been quite disappointed by stuff like tern[1]
Sometimes (especially when concurrency is involved) this is not feasible, yet debugging is still possible. These are the cases that really test a developer's skills.
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.
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 configuration (so you can test program subsystems in isolation or in mock environment), use better tools, use toos for automatic verification of code, etc.
Your code is your debugger. If you will write properly, you will need not to run debugger at all, because code will explain problem to you (or to future user or administrator of your program).
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.
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.
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. What has changed? What could have caused this bug to appear?
2. Verify fundamentals: Is it actually running the code I think it does. If I change something that should change something, does it change? If I break something does it break?
3. Verify Input: Make sure it's valid
4. Verify output
5. Timing issues?
6. Slowly remove code "around" bug until it works again
7. Or start back at something you know works and add back code until it breaks.
8. Break out the functionality into an isolated environment so you can test it separately so you'll know if the problem is within the feature or a side-effect of something else
9. If your code makes it too complicated to see and find the bug refactor until it isn't.
10. Go to sleep / Go on a walk. Let your subconscious process it
and finally, lot's of bugs are made self evident by well structured and refactored code, so don't get too busy working on the symptoms rather than the root cause.