GitHub Fundamentals
Topic 33

Issues

GitHub

Issues are GitHub's tracker for bugs, tasks, and discussion — lightweight tickets that link directly to commits, pull requests, and code. The point of keeping them on GitHub rather than in a separate tracker is that the conversation lives next to the work it describes.

An issue is cheap to open and cheap to link, which is its strength and its trap. Used with a little structure — templates, labels, milestones — it becomes the searchable record of why work happened. Used as a blank textarea, it becomes a pile of half-described bug reports.

The Issue Object

Each issue has a title, a Markdown body, a state (open or closed), and a timeline. The timeline is the part people overlook: it records every label change, assignment, reference, and cross-link as a permanent event log, so the history of how an issue was triaged is recoverable, not just its current state.

Because the body is Markdown, you can embed checklists, code blocks, and links to specific lines of code. A reference like #123 from a commit or another issue shows up automatically in this issue's timeline, wiring the conversation together without manual bookkeeping.

Labels, Milestones, Assignees

Labels categorize, milestones group toward a release, and assignees mark ownership. These are not interchangeable: a label is a many-to-many tag you filter on, a milestone is a one-to-many bucket with a due date and a progress bar, and an assignee is a person, not a status.

Treating a milestone as just another label breaks the release view, because a milestone reports completion percentage across its issues. Treating assignees as the source of truth for status breaks filtering, because "all in-progress bugs" is a label query, not something you can derive from who happens to be assigned.

Issue Forms and Templates

The .github/ISSUE_TEMPLATE/ directory holds templates, and YAML issue forms go a step further by enforcing structured fields — version, reproduction steps, environment — instead of a blank body. A reporter fills in required fields rather than remembering what to include.

Forms pay for themselves in triage cost. A bug report that arrives with the version and repro steps already present skips an entire round of back-and-forth, which is the most expensive part of handling a vague report.

Linking and Closing

A closing keyword — Fixes #123 or Closes #123 — auto-closes the referenced issue when the change lands, but only when it appears in a pull request body or in a commit on the default branch. The same words in a stray comment do nothing.

Task lists in an issue body (- [ ] items) track sub-work and roll up into a visible count, which is how you split a larger piece of work into checkable steps without opening five separate issues that all have to be closed together.

Search and Triage

Issue search is a query language: is:open label:bug no:assignee finds open bugs nobody owns, and filters like these can be saved. Building triage around consistent labels is what makes these queries answer real questions instead of returning noise.

Common Mistakes
  • Accepting bug reports through a blank issue body with no form, so they arrive without version, repro steps, or environment and double the triage time.
  • Writing Fixed #123 in a stray comment instead of the PR description, so the keyword never auto-closes the issue — closing only fires from the PR body or a commit on the default branch.
  • Overloading one issue with five unrelated bugs, so it cannot close until all five are done and never links cleanly to a single PR.
  • Relying on assignees as the source of truth for status with no labels, so you cannot filter "all in-progress bugs" because state lives only in people's heads.
  • Treating a milestone as a label for categories, which breaks the release view since a milestone is a dated bucket with a progress bar.
Best Practices
  • Define issue forms in .github/ISSUE_TEMPLATE/ with required fields for version and reproduction steps.
  • Use a small, deliberate label taxonomy — type, priority, area — and apply it consistently so filters work.
  • Close issues from PRs with Closes #N in the PR description so the link and the close happen on merge.
  • Group issues into milestones tied to a release date to get an automatic completion percentage.
  • Add a config.yml in the template directory to route security reports to a private channel instead of public issues.
Comparable toolsGitLab issues with boards and milestonesBitbucket issues, or Jira integrationGitea issues with labels and milestonesAzure DevOps work items on Boards

Knowledge Check

Where must a closing keyword like Closes #123 appear to actually auto-close the issue?

  • In a pull request body or a commit on the default branch — not in a stray comment
  • Anywhere the words appear, including any comment a contributor leaves on the issue
  • Only in the issue's own title, where GitHub scans for closing keywords
  • In a label named after the issue number it should close on merge

How does a milestone differ from a label?

  • A milestone is a one-to-many bucket with a due date and a progress bar; a label is a tag you filter on
  • A milestone can be applied to many issues at once, whereas a label is strictly restricted to a single issue at a time and cannot be reused
  • They are the same underlying feature, merely presented under two different names in the issues UI
  • A label is what carries the due date and the completion progress bar, while a milestone is just a plain filterable tag

Why do issue forms reduce triage cost?

  • They enforce required fields like version and repro steps, so reports arrive complete and skip a round of back-and-forth
  • They automatically assign each new issue to the right engineer and route it to the correct team based on which fields the reporter filled in
  • They detect and automatically close duplicate reports before the reporter ever finishes opening them
  • They convert the submitted issue into a draft pull request automatically, with a branch already created

What is the tradeoff of one big issue holding five unrelated bugs?

  • It cannot close until all five are done and never links cleanly to a single PR
  • GitHub caps an issue at three cross-references, so two of the five PR links are silently lost
  • Milestones refuse to accept any issue that carries more than one label at a time
  • The timeline stops recording new events once the first of the five bugs is described

You got correct