How to attach code pointers to a customer bug report automatically
Yes, for the common case, and it works today with nothing exotic. GitHub's own search API can look up files, pull requests, and past issues from the text of a new bug report, and a small script can post the results as a comment before anyone is assigned. The trigger is a webhook, the lookup is two API calls, and the whole thing runs in the seconds between a customer's message landing and an engineer opening the ticket.
The catch is what "the common case" means. This works when the report shares vocabulary with your code, things like a stack trace, a function name, an error string, or a file path pasted from a log. It works much less well when a customer describes a symptom in their own words and none of those words appear anywhere in your repo. That gap is most of this guide.
What "before an engineer opens it" actually requires
Four pieces, none of them large:
- A trigger. GitHub's
issueswebhook fires onopened, and the payload carries the issue's title, body, and number along with the repository it belongs to. That's the starting gun. If bug reports arrive somewhere else first, a Zendesk or Linear ticket, the same idea applies once that system's webhook hands off to the script; GitHub only has to be the place the code pointers come from. - Terms worth searching on. Pull the obvious candidates out of the report text, anything in backticks or quotes, a camelCase or snake_case identifier, a file extension, or a line that looks like a stack trace frame. This is string matching, not language understanding, and it's deliberately cheap.
- Two API calls. GitHub's search code endpoint takes those terms and returns matching files, and the search issues and pull requests endpoint takes the same terms and returns prior reports and the PRs that touched them. Both are qualifier-based (
repo:,is:issue,is:pull-request,state:closed), so the query for "past reports of this" is close toverifySignature is:issue. - A pointer written down before triage. The script posts a comment on the new issue with whatever it found, the file path, the PR number, the closed issue number. Nobody has to search for it, because it's already sitting on the ticket when they open it.
Two limits are worth building around rather than discovering later. Code search only looks at your default branch, so a pointer to code on a long-lived feature branch won't show up. Code search is also the one to watch on rate limits, 10 requests a minute for authenticated search, plenty for one bug report but not for a script also running on every issue comment and every reopen. Issue and PR search is looser, 30 requests a minute, so it rarely queues before code search does.
The reports that keyword matching misses
The setup above works when the customer's language and the code's language happen to overlap. Most reports don't get that lucky:
- Vague symptoms share no vocabulary with code. "Payments feel slower than usual" has nothing in it that
search/codecan use. No file is namedslower. - Renamed functions break old matches. If
verifySignaturegets renamed tocheckSignaturein a later refactor, every future report using the old name misses the file it should have hit, silently. - Closed issues phrased differently don't turn up. A report that says "requests time out on retry" won't match a closed issue titled "signature check breaks on retried events," even though they're the same bug, because
search/issuesis matching literal terms, not meaning. - The code search rate limit bites at real volume. Ten searches a minute sounds like a lot until a support-facing repo gets a burst of similar reports after an incident, and the fourth or fifth comment in that window queues instead of posting.
None of that is a reason to skip the script. It's a reason to expect it to go quiet on exactly the reports where a pointer would have helped most, the ones described in plain language by someone who doesn't know the codebase's naming.
Where this becomes Modem's job instead
Modem doesn't search file contents either. Its GitHub integration reads issue and PR metadata from the repos you connect, not the code itself. What it changes is the matching step upstream of that. A GitHub issue captured by Modem joins the same topic as every other report of the underlying bug, however each one was worded, because the classification runs on meaning rather than shared substrings. When a PR eventually merges against that topic, Modem matches it back automatically and surfaces who to follow up with, so the connection between "customer said X" and "PR fixed Y" survives even when the two never shared a single keyword.
That's a real difference from the script above, and a narrower one than it might sound. The script finds files and PRs for the reports that happen to name them. Modem finds the other report of the same bug regardless of wording, and keeps the two linked through to the fix. Teams running one script per repo, on one straightforward product, rarely need both. The math changes once reports about the same underlying issue arrive worded three different ways across GitHub, a support inbox, and Slack, and a keyword search run three separate times catches none of them as the same thing. I'm on the Modem team, which is worth knowing before taking that comparison at face value. Read fairly, the script above costs nothing to build, and that's the bar a platform actually has to clear.
A related question once code pointers exist on a ticket is whether the same bug is quietly being filed twice, covered in can GitHub detect duplicate issues before they pile up, and what happens after an engineer has the pointers, covered in how to have your coding agent fix user-reported bugs.
Wire the script before reaching for a platform
Wire the webhook, pull terms out of the report text, run both search endpoints, post a comment. That's an afternoon of work and it already beats the default, which is an engineer opening a fresh issue with nothing attached and starting from zero. Add the platform layer only once plain-language reports that share no vocabulary with your code are the ones actually piling up unmatched.
