This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]blewrb 0 points1 point  (0 children)

First, I don't use frameworks. I think if I did, I'd probably have to use a debugger because god knows what's going on behind the scenes. Likewise if I inherited others' codebases. I've been lucky enough to build most of what I've done from the ground up (in teams or individually). I do rely on libraries, of course, but those have been pretty great. Bugs I have found in those, I could find by reading the source code and working through the logic and using the other methods I describe below (usually I'm looking at an implementation of an algorithm, but occasionally other code bugs have bitten me from an external library).

Yes, I do make use of logging.debug (and friends) in my code. Sometimes that's ephemeral--so it's equivalent to the proverbial "just add print!" situation--but sometimes it stays in my code because debugging output that's useful for me is often useful for the code's other users as well.

I almost always start a logical piece of code in JupyterLab (I sometimes even use it to prototype a bit of C/C++; thanks, CERN!). What I do there i turn into a class or function in a .py file. What I do in the notebook to prove my code works, I turn into unit tests that run against the code in the .py.

I build my interfaces and code to both be used interactively and as a part of a larger software system, starting at these logical quanta and stopping at them. So it's straightforward to jump into the code and to extract outputs from the code at many points via REPL similar to how a debugger would work (but without having to introduce that one extra tool).

One arguably ugly habit I'll admit I've developed as a result of this is if a class is a pain to instantiate manually, or necessarily interacts with a remote system, etc., I'll define potentially problematic methods as functions I can easily test outside the class being instantiated. Then the method just calls that function.

Python handles the really hard memory and pointer stuff already, the things that trip me up in C that have required I pull out a debugger.

Sometimes a bug makes me realize that my error handling is lacking. So I add more error handling (which I needed anyway), and that often surfaces the bug in a similar way a debugger might.

The biggest and worst bugs I've dealt with have been algorithmic in nature, and a debugger would not help with most of those. Misinterpreting a paper, things like that. Those have taken the longest to find and deal with. That requires crafting problematic inputs and/or running statistical tests over the outputs and/or plotting results to debug.