How to triage GitHub issues at scale
An issue tracker with 40 open issues doesn't need a triage process. One with 400 does, and by the time you notice, the backlog is already full of duplicates, stale reproductions, and bugs nobody has read. The teams that keep large trackers useful all converge on the same structure: every new issue enters a known state, a small set of labels moves it through, and someone sweeps on a schedule.
The Kubernetes triage guide is the best public reference for this at extreme scale; most of what follows is that playbook cut down for a team of five to fifty.
Step 1: give every new issue a default state
The core mechanism is a label that means "no human has looked at this yet." Kubernetes uses needs-triage, applied automatically to every new issue; triage means removing it and replacing it with something meaningful. Do the same with a GitHub Action or a repo workflow — the marketplace has several that apply labels on open.
This turns "have we looked at everything?" from a judgment call into a filter: is:open label:needs-triage. If that filter is empty, triage is done. If it has 60 items, you know exactly how far behind you are.
Step 2: use a taxonomy small enough to apply in ten seconds
Label sprawl kills triage because nobody can hold 80 labels in their head. You need three axes, and only three:
- Kind:
bug,feature,question,docs. One of these, always. - Priority: Kubernetes uses
priority/critical-urgent(drop everything),priority/important-soon,priority/important-longterm, andpriority/backlog— see the community triage doc. Four levels is enough; more than four and the middle ones become meaningless. - State:
needs-more-infowhen the report can't be acted on as written,confirmedonce someone reproduces it.
A triager should be able to process an issue by answering three questions: what kind is it, how urgent is it, can we act on it as written. If your labels require more thought than that, cut them.
Step 3: make duplicates a merge, not a close
At scale, duplicates are most of the noise. The failure mode is closing them with "dupe of #1234" and losing the signal — the fifth report of a bug is information about severity, and the person who filed it is someone you should tell when it's fixed.
So the habit is: close the duplicate, but leave a comment linking the canonical issue, and record the reporter on the canonical issue (a comment like "also reported by @acme-dev in #2101" is the minimal version). When the fix lands, everyone who hit the bug can be told, not just the first reporter. If your issues come partly from customers, this is where triage connects to closing the feedback loop.
Step 4: triage on a schedule, not on arrival
Triaging each issue as it arrives feels responsive and doesn't scale — it fragments the day and the standards drift by triager. The pattern that holds up is a sweep: a rotating person (or pair) clears the needs-triage filter on a fixed cadence — daily for high-volume repos, twice a week otherwise. Kubernetes suggests starting with the oldest unlabelled issues first, which stops the backlog's tail from rotting while new issues get attention.
Give the rotation a written rubric — a one-page doc with the label definitions and two or three worked examples ("user says the CLI hangs on M-series Macs, no repro steps: bug + needs-more-info, ask for --verbose output"). The rubric is what keeps ten different triagers producing one consistent tracker.
Step 5: automate the mechanical parts
Three automations pay for themselves immediately:
- Auto-label on open (step 1's
needs-triage). - Stale nudges: an action that pings
needs-more-infoissues after 14 quiet days and closes them after 30. Reopening is cheap; a tracker full of unanswerable reports is not. - First-pass classification: LLM-based triage bots that read a new issue against the codebase and suggest kind, priority, and likely duplicates have become genuinely usable — GitHub's own agentic issue-triage workflow is one example of the pattern. Treat suggestions as suggestions; the sweep still confirms them.
For the subset of issues that are really customer feedback in disguise — feature requests filed by users, bugs escalated from support — we build Modem, which watches connected repos alongside Slack and support tools, dedupes the same request across all of them, and keeps the requester list attached so the follow-up in step 3 happens automatically. The result is a context graph of topics, people, and accounts that a triage bot can query over MCP directly, instead of re-reading issue threads to reconstruct who reported what. It doesn't replace repo triage for internal engineering issues; it covers the customer-facing slice. More on the automated approach in our guide to AI triage tools.
What the steady state looks like
A healthy tracker at scale has: zero issues older than a week still in needs-triage, every open bug carrying a kind and a priority, duplicates closed with their reporters preserved, and a rotation doc that a new team member can execute on day one.
The smallest version you can start this week: add the needs-triage auto-label, define four priority labels, and put a 30-minute sweep on one person's calendar for Friday. That alone converts an unbounded backlog into a bounded queue.
