Rubber-duck debugging works for me, but I've always lost the useful part. I talk through the problem, I find the bug, and an hour later I can't explain to my team how I found it. Last month I tried recording the monologue and running the audio to text afterwards. The transcript turned into the first draft of my bug report.
This is what happened, what the script looks like, and where it fell short.
The bug: a test that failed only after 4 p.m.
We had a test in a Node service that checked "orders created today." It passed in CI every morning and failed for anyone who ran it after about 4 p.m. Pacific.
You can probably guess the cause. I couldn't, at the time, and I spent forty minutes going in circles.
Recording myself (and only myself)
I work from home, so the only voice on the recording was mine. That matters. If you try this while pairing, ask your pair first, and stop recording when someone else drops into the call without knowing.
I didn't bother with a separate app. The tool I used has in-browser recording, so I opened a tab, hit record, and started talking at my second monitor like a person who has been alone too long.
The one rule I gave myself: say a marker word out loud before anything worth keeping.
- "Hypothesis:" when I had a theory
- "Ruled out:" when a theory died
- "Note:" for anything I'd want in the write-up
- "Fix:" for the final change
It felt silly for about three minutes. Then I forgot I was doing it.
Forty minutes of talking, condensed
Here's a cleaned-up slice of the transcript, to give you the flavor:
Hypothesis: the fixture dates are hard-coded and drift. … Ruled out: fixtures use
Date.now(). … Okay, so it fails at 4:10 and passes at 3:50. Note: that's suspiciously close to midnight UTC. Hypothesis:startOfDayis running in UTC on my machine but the query uses local time. … Fix: pass the tenant timezone intostartOfDayinstead of relying on the process TZ.
Pacific time is UTC−8 in winter, so 4 p.m. local is midnight UTC. Once I said that sentence out loud, the bug was obvious. The recording caught the exact moment, which is what I always lose.
The audio to text part
When I stopped the recording, I ran the transcription right there in the browser. The free guest run covers up to 60 minutes once, which was plenty for a 40-minute ramble. Since then I've signed in. In Standard mode without speaker identification, each minute costs one of the 240 daily free credits, and a solo monologue doesn't need speakers anyway.
If you want to try it on your own next debugging session, you can record and transcribe in the browser without installing anything, then export the transcript as a TXT file.
The transcript wasn't perfect. startOfDay came back as "start of day," and TZ as "tease." Accuracy is up to 99% on clear recordings, and me mumbling at a monitor with a fan running is not that. I fixed the code identifiers by hand with the editor's search before exporting. It took about three minutes.
The 35-line script
The export is plain text. I wrote a small Python script that splits it into sentences and groups anything that starts with a marker word. No dependencies.
#!/usr/bin/env python3 """Pull marker sentences out of a spoken debugging transcript.""" import re import sys from pathlib import Path MARKERS = ["hypothesis", "ruled out", "note", "fix"] def sentences(text: str): # Split on ., ?, ! followed by whitespace. Good enough for speech. return [s.strip() for s in re.split(r"(?<=[.?!])\s+", text) if s.strip()] def extract(text: str): found = {m: [] for m in MARKERS} pattern = re.compile(r"^(hypothesis|ruled out|note|fix)\s*[:,-]?\s*(.+)", re.I) for s in sentences(text): m = pattern.match(s) if m: found[m.group(1).lower()].append(m.group(2)) return found def to_markdown(found): titles = {"hypothesis": "Hypotheses tried", "ruled out": "Ruled out", "note": "Notes", "fix": "Fix"} out = ["# Bug report (draft)", ""] for key in MARKERS: out.append(f"## {titles[key]}") out += [f"- {line}" for line in found[key]] or ["- (none)"] out.append("") return "\n".join(out) if __name__ == "__main__": text = Path(sys.argv[1]).read_text(encoding="utf-8") print(to_markdown(extract(text))) Run it like this:
python3 duck.py debugging-2026-10-14.txt > bug-report-draft.md The output for my timezone bug looked like this (trimmed):
## Hypotheses tried - the fixture dates are hard-coded and drift. - startOfDay is running in UTC on my machine but the query uses local time. ## Ruled out - fixtures use Date.now(). ## Notes - that's suspiciously close to midnight UTC. ## Fix - pass the tenant timezone into startOfDay instead of relying on the process TZ. That went into the PR description almost unchanged. My reviewer said it was the first bug write-up of mine she'd actually enjoyed reading.
Where it fell short
A few honest caveats after doing this about a dozen times:
- Markers get mangled. "Ruled out" occasionally became "rule doubt." I added a fuzzy match for that one, then gave up and just fix it by hand in the editor.
- Speech doesn't punctuate like writing. Some "sentences" run for 80 words. The regex split is crude. It's fine for a draft and useless for anything you'd publish as-is.
- Code doesn't survive speech. Never trust a transcribed identifier. I read every backticked name against the actual code.
- Not every session is worth it. For a five-minute bug, this is overkill. I use it when I've been stuck for 20 minutes and can feel myself starting to loop.
Why I kept doing it
The script isn't the point, and neither is the audio to text step on its own. The point is that speaking a hypothesis out loud forces me to finish the thought, and the recording means I don't lose it.
I also noticed a pattern in my own transcripts: I say "Ruled out" far less often than "Hypothesis." I jump to the next theory without killing the last one. Seeing that on paper has made me a slightly more disciplined debugger.
If you try it, I'd love to know what marker words you end up using. Mine have grown to include "Weird:", which so far has preceded every real bug I've found.
Disclosure: I'm involved with AudioToText, the transcription tool used in this post. The bug, the script and the opinions are my own.

