Rubber-Duck Notes That Unlock Stuck Debugging

.duck-notes.md lives in the root of every active project directory you work on. It’s a lightweight, structured rubber-duck debugging template you fill out the second you hit a bug that stumps you for longer than 10 minutes, designed to cut out the rambling, circular guesswork that eats hours of development time. Unlike unstructured note files or random comments in your code, it forces you to ground your debugging in verifiable facts before you make a single change to your codebase.

The broken line

This section anchors your entire debugging session to the exact, specific point of failure, so you don’t drift into fixing unrelated issues or making assumptions about what’s broken before you have evidence. You start every entry here by writing the exact line number, error message, or unmet behavior you’re observing, with no interpretation or guesswork. If your React form fails to submit when tapped on iOS Safari, you don’t write “form is broken” — you write “Line 147 of src/components/SubmitForm.tsx: preventDefault() runs on tap, but no POST request appears in the network tab of iOS Safari 17.4 dev tools, no CORS or 4xx/5xx errors are logged.” This single line eliminates the most common debugging mistake: working to fix a problem you haven’t clearly defined first. Many developers waste 20 minutes or more changing code that has no connection to the actual bug, simply because they never took 30 seconds to write down exactly what was failing.

Rubber-Duck Notes That Unlock Stuck Debugging desk detail
Desk detail for this page — not a measured lab photo.

What you leave alone

This section exists to stop you from looping back to parts of the codebase you’ve already ruled out as the source of the bug, a common habit when frustration sets in. For every debugging session, you list all functionality, code paths, and dependencies you have already verified work as expected, so you don’t waste time re-testing them over and over. If you’ve already confirmed your API route accepts POST requests from all origins, that your form state updates correctly on user input, and that the same form works perfectly on desktop Chrome, you list all three here. You can also add links to passing test runs, staging environment demos, or commit logs that prove these parts of the codebase are not the issue. This cuts down on the “what if I missed something?” panic that makes you re-check the same API route for the 12th time, even when you already know it works.

The edit

This is the core of the file, where you fill out the four standard fields that make up the rubber-duck note template. The table below lays out each field, what it requires, and a sample entry for a common slow API endpoint bug:

Field Description Example Entry
Expected The exact, measurable behavior the code is supposed to produce, no vague language “GET requests to /api/invoices return a 200 OK response with full invoice data in under 200ms for authenticated users”
Actual The verified, observed behavior you are seeing when you run the code, with any relevant metrics or error messages “GET requests to /api/invoices return 200 OK but have an average 1.2s response time when the connected database holds 10 or more invoice records, no error messages appear in server logs”
Last Change The most recent commit, code edit, or dependency update that touched the relevant feature or code path, with a reference if possible “Added nested SQL join for invoice line items to the /api/invoices query in commit d7f2a9c 3 days ago, no other changes to the endpoint have been merged since”
Next Probe The single, specific test or change you will run next to isolate the cause of the bug, no broad lists of possible fixes “Run EXPLAIN ANALYZE on the modified invoices SQL query to check for missing indexes on the line_items.invoice_id foreign key column”

Illustrative example: For the sample endpoint bug in the table above, you’d fill out all four fields in less than two minutes, then immediately run the next probe you listed instead of scrolling through Stack Overflow for random SQL optimization tips. This template structure is adapted from the debugging workflows used in Build Next Stack project templates to cut average debugging time by 60% for new full-stack projects. The key rule here is that you only fill out one next probe at a time: you don’t list 10 possible fixes to try, because that encourages you to jump between random changes without verifying the impact of each one. If your first probe doesn’t uncover the issue, you come back to this section, update the fields with any new information you learned, and write a single new next probe to try.

Illustrative card for Rubber-Duck Notes That Unlock Stuck Debugging
Illustrative worksheet for this topic. Treat numbers as examples.

Where the file goes

This file never gets committed to shared version control, so you should add `.duck-notes.md` to your global .gitignore file to ensure it stays local to your machine for every project. It is not a team documentation file, nor is it a permanent record of bugs for the repo — it is your personal, disposable debugging scratchpad. The only time you share its contents is when you ask a teammate for help, so they can get up to speed on your bug in 10 seconds instead of spending 10 minutes asking you to walk through what you’ve already tried. If you work across multiple devices, you can sync the file via a private cloud drive folder so you can pick up a debugging session where you left off on a different machine, but you should never add it to a public or shared repo, as it will clutter the codebase with personal work notes that have no value to the rest of your team.

What you do not add

Guardrails keep this file useful, so there are four hard rules for what you never include in its contents. First, no rants about how stupid the bug is or how frustrated you are: emotional language keeps you in a frustrated headspace and wastes time you could spend isolating the issue. Second, no tangents about unrelated features or refactors you thought of while debugging: save those for a separate project ideas note file, so you don’t get distracted from the bug you’re actively working to fix. Third, no unconfirmed assumptions about what’s causing the bug: every entry has to be verifiable with a test, dev tool readout, or commit log, so you don’t waste time fixing a problem that only exists in your head. Fourth, no long lists of possible fixes to try randomly: only the single next probe you’re going to run makes it into the file, so you don’t jump between 8 different changes and lose track of what modified which behavior.

The next time you open this file

You open this file every single time you hit a bug that stumps you for longer than 10 minutes, no exceptions. You don’t wait until you’ve wasted an hour guessing at fixes to fill it out, and you don’t skip it for “small” bugs that you think you can fix in your head. When you open it, you first erase the contents of the previous debugging session to start with a blank slate for the new bug, unless you’re picking up a bug you left off on the day before, in which case you start by re-reading the last entry to get back up to speed without redoing any work you already completed. After you fix the bug, you can leave the entry in the file for 24 hours in case a related bug pops up, then erase it the next time you start work so the file stays clean and uncluttered. If you run into the same type of bug across multiple projects, you can add a short, anonymized note about the fix to a separate personal knowledge base file, but you don’t store old debugging entries in this file long term. Over time, filling out this file becomes a habit that trains you to think more systematically about debugging, even when you don’t have the file open, because you’ll automatically start asking yourself the same four questions the template uses whenever you hit a broken piece of code.