Debugging is a very useful tools for inspecting the state of the objects and variables in your code at run time.
As previously mentioned in the answers above, debugging is extremely helpful, but there are some cases where it is limited.
In my experience, I find using the debugger to be very useful because it helps to reveal false assumptions that I was making about the state of my code. Some people aren't as astute at reading through the code to find a bug, so debugging can help in revealing false assumptions that you or another developer made about the state of the code.
Maybe you expect that a parameter will never be null when passed to a method, so you never check for that case and carry on in the method as if that parameter will never be null. The reality is that parameter will end up being null at some point even if you set as a pre-condition to the method that the parameter should never be null. It always will happen.
In contrast to debuggers' usefulness in the aforementioned examples, I find it difficult and somewhat not useful to use when multi-threading (i.e., concurrency, asynchronous processing) is involved. It can help, but it is easy to lose your orientation in the multi-threaded fog when the debugger's breakpoints are being hit in one thread at point A and a completely separate thread at point B. The developer is forced to push the new breakpoint "thought process" on the top of his brain's "stack" and orient himself to the code at the point of the new breakpoint. After the relevancy of breakpoint B decreases, the developer then switches back to the first breakpoint, and has to recall what he/she was looking out for before the trigger of breakpoint B. I know that this may be a confusing explanation, but my point in this paragraph is that debugging where concurrency is used can be a very A.D.D. (Attention Deficit Disorder) process, and so it may be more difficult to remain productive in your debugging thought pattern.
Also the unpredictability of concurrent code can further distract the developer in debugging concurrent code.
In conclusion, in my honest opinion:
- Debugging when concurrency is used = increased tendency to lose focus of "debugging thought pattern"
- anytime else = increased debugging productivity b/c your attention isn't interrupted by unexpected breakpoints (unexpected due to race conditions).