Screencast about my bug fixing workflow

Hot on the heels of my second screencast, here’s my third. This one covers how I go about fixing bugs. In particular, it shows how I fixed core.memoize #26, using my day-to-day workflow in Atom, Chlorine, and REBL. It shows how I use tap> for debugging, and how I use the steps from the ticket to reproduce the problem and then create an automated test for the issue.

YouTube: Fixing a bug in core.memoize (27 minutes)

5 Likes

Thanks Sean, that’s interesting. I think it’s very valuable for beginners in particular to share our practical dev workflows, as these can’t be well conveyed with words.

I’m curious as to why you find tap> particularly compelling for doing this, compared for instance to the other techniques listed here, e.g the inline-def technique or using scope-capture or some in-editor equivalent?

What seems suboptimal to me about tap> is that it lets you basically save values on the fly, but does not let you recreate the lexical scope which created them; re-creating their scope means you can improvise several in-situ experiments without having to re-run the calling code, and without having to anticipate what values you want to observe in tap>.

Cheers!

Definitely good questions!

I consider tap> + REBL to be a better version of the first two techniques: printing in-flight values and “spying”, because you can a) get the tap> output separate from regular print output and b) process the tap> programmatically and, with REBL, browse it as navigable data structures.

I haven’t found tracing libraries terrible useful so far (eight years of production Clojure) due to the signal to noise ratio but I can imagine them being very useful if you genuinely don’t understand the flow of control in a codebase well enough to pinpoint where to add some sort of print/spy function.

I think tap> + REBL gives me most of what “inline def” provides: tangible, browsable access to a local value – but I do have a hotkey in my editor to turn symbol/expression pairs in let bindings into top-level defs as well, for when I need to eval forms in a function that depend on other local symbols.

I have not, yet, found a need for a tool like scope-capture. ProtoREPL had something similar built-in and I used it maybe once in a year or so? I don’t have that in Chlorine (beyond the shortcut to turn let bindings into defs) and I haven’t missed it so far.

Something that REBL provides, that I’m finding very useful with tap> (and I show it briefly in that screencast) is the nav-> feature: I can add code that REBL applies as threaded navigation as I scroll through a series of tap> results. That let’s you put code from your function into REBL and use the tap> history to evaluate a specific form repeatedly for all of the calls. A bit like mapping letsc across the captured history.

1 Like