Chapter 05Lesson 01~115 minutes

Issues, Labels, Milestones, Templates, Forms, Discussions, and Triage: Concepts, Architecture, and Mental Model

Build a disciplined intake model in which GitHub Issues become structured, queryable work records—distinct from Git history—and learn how labels, milestones, templates/forms, Discussions, permissions, and moderation fit together.

IssuesTriageIssue formsDiscussions

Learning objectives

  • Explain why a GitHub issue is hosted work/conversation state rather than a Git object or branch.
  • Relate lifecycle state, assignees, labels, milestones, issue types, and cross-references without treating them as interchangeable metadata.
  • Distinguish Markdown issue templates from YAML issue forms and explain where config.yml participates in intake.
  • Choose between an Issue and a Discussion based on whether the team needs actionable ownership and tracked work versus open-ended community conversation.
  • Describe triage, write, maintain, and admin boundaries for common issue-management actions.
  • Inspect current issue state through the web UI, structured gh output, and versioned REST responses before mutation.
Availability: The mandatory path works with GitHub.com, a personal GitHub Free account, and a disposable public repository. Issue forms are currently documented by GitHub as public preview and subject to change. Organization-defined issue types/issue fields are optional extensions; the personal-repository path uses labels and milestones instead. Discussions can be enabled on public or private repositories when the learner has sufficient repository permissions.

1. The problem: “an issue exists” is not the same as “work is managed”

A repository with hundreds of titles such as “does not work,” “question,” and “urgent” has records but not an operating system. An on-call engineer cannot quickly answer: Which reports are reproducible? Which are incidents? Who owns each item? What must ship together? Which item is canonical when five people report the same failure?

GitHub Issues solves only part of that problem. The platform gives you a work record, comments, state, metadata, references, and permissions. Your team still has to design an intake contract and a triage policy that turns incoming text into consistently queryable decisions.

Mental model: an Issue is a hosted record that accumulates work state and conversation around a repository. Git commits may later reference or close it, but the issue itself is not stored in .git, does not travel with git clone, and is not a branch.

2. Re-anchor the Git boundary from Chapters 01–04

Object or state Where it lives Does git clone copy it? Typical owner
Commit / tree / blob / Git ref Git repository object/ref database Yes, subject to clone/ref selection Git repository
GitHub Issue + comments GitHub hosted database No GitHub repository
Label / milestone / assignee GitHub hosted metadata No GitHub repository / account permissions
Issue form/template file Git repository file on default branch Yes, as repository content Repository maintainers
Discussion + category GitHub hosted community state No Repository/organization discussion space

Notice the useful split: the configuration for issue forms can be versioned as files, while the issues produced from those forms are hosted records. This is why form changes can be reviewed like code while the resulting triage queue is queried through GitHub.

3. One intake record, several independent dimensions

Issue intake and triage flow
flowchart LR
  R[Reporter] --> I[Issue record]
  F[Template / issue form
on default branch] --> I
  I --> A[Assignee
ownership]
  I --> L[Labels
classification]
  I --> M[Milestone
time/release grouping]
  I --> T[Issue type
organization classification
when available]
  I --> S[Open / closed
lifecycle state]
  I --> X[Links / references
PRs, issues, commits]
  I --> D[Discussion
if conversation is not actionable work]

The arrow from the template/form means the intake UI influences the shape of the issue body and optional defaults. The other arrows are hosted metadata relationships. None of them moves a Git branch. The Discussion arrow is a routing decision: some conversations should leave the actionable work queue entirely.

4. Issues are work and conversation records

A useful issue has an identity (repository + issue number), title/body, author, timestamps, open/closed state, comments, timeline events, and optional metadata. It can be linked from another issue or pull request, mentioned by commit/PR text, and queried later. The critical operational property is not that it contains prose—it is that the record can accumulate auditable decisions over time.

GitHub’s REST model shares several issue endpoints with pull requests because every pull request is represented as an issue for common metadata such as assignees, labels, and milestones. When using raw REST collection endpoints, automation must therefore check for the pull_request field if it intends to process only ordinary issues.

5. Labels, milestones, assignees, and issue types answer different questions

Mechanism Question it should answer Scope / ownership Common misuse
Assignee Who is currently expected to act? People eligible for assignment in repository context Using assignment as priority
Label What category/state signal helps filtering? Repository-local label definition Hundreds of near-synonyms
Milestone Which bounded goal/release/timebox groups this item? Repository milestone One milestone per team/status forever
Issue type What organization-wide class is this work? Organization-defined; inherited by repositories where available Assuming personal repos have org issue types
Open / closed + reason Is this record active, completed, duplicate, or not planned? Issue lifecycle Closing without documenting why/canonical link

Current GitHub organization roles make an important distinction: triage access can apply/dismiss labels, apply milestones, mark duplicates, and manage many issue states, but creating/editing labels or milestones requires write access. Issue types are configured by organization owners. This is least privilege in practice: a triager does not need code-push rights merely to classify incoming work.

6. Markdown templates versus YAML issue forms

Choice What contributor experiences Validation strength Best fit
Free-form issue Blank title/body None beyond normal issue creation Trusted teams, unusual work, maintainer-only escape hatch
Markdown issue template (.md) Pre-filled Markdown structure Guidance, not enforced field validation Flexible reports that still need headings/prompts
YAML issue form (.yml) Rendered web form with inputs/dropdowns/checkboxes/uploads Can mark individual inputs/options required Repeatable intake where missing reproduction/context is costly

GitHub currently labels issue forms as public preview. Forms are stored in .github/ISSUE_TEMPLATE on the default branch. Required keys include name, description, and body; optional keys can apply a title prefix, labels, assignees, issue type, or projects when those targets exist and permissions permit them.

Validation is not diagnosis. A required “Steps to reproduce” field guarantees that the reporter enters something; it does not guarantee the steps are correct, complete, safe, or sufficient. Human triage still decides actionability.

7. config.yml controls the chooser, not the content of each form

The file .github/ISSUE_TEMPLATE/config.yml can disable the ordinary blank-issue option for contributors and add contact_links that route support or security reports elsewhere. GitHub documents an important exception: when blank_issues_enabled: false, users with Write/Maintain/Admin still see a “Maintainers only” blank option, while Read/Triage contributors see only configured templates.

blank_issues_enabled: false
contact_links:
  - name: Community questions
    url: https://github.com/OWNER/REPO/discussions
    about: Ask usage and design questions here instead of opening tracked work.

The chooser configuration becomes active when it is merged into the default branch. That is a hosted UI effect caused by a versioned Git change—an excellent example of the Git/GitHub boundary.

8. Discussion versus Issue: conversation is not always work

GitHub Discussions is designed for community conversations, Q&A, announcements, and open-ended ideas that do not need to be tracked as repository work. Issues are better when the team needs a concrete action, owner, triage state, reproducibility evidence, or linkage to implementation.

Scenario Prefer Why
“How do I configure this library on Windows?” Discussion / support channel Question may benefit from searchable community answers; no code change is necessarily committed
“API returns 500 for this exact request on v2.4” Issue Actionable defect with reproduction and ownership
“Production checkout is down now” Incident system + tracked Issue if repository work is needed Operational response needs explicit ownership/escalation; a Discussion is too conversational
Early “what if we redesigned X?” idea Discussion initially Conversation can mature before becoming committed work

When Discussions is enabled, people with triage access can moderate discussions and can convert an issue into a discussion. The conversion creates a discussion using the issue content; it is a routing operation, so record why the item is no longer actionable work.

9. Duplicate, pin, transfer, lock: different administrative meanings

These actions should not be conflated. Marking a duplicate says “another record is canonical.” Pinning raises visibility. Transferring says “the work belongs in a different repository.” Locking is moderation to stop or constrain conversation; it is not a normal lifecycle state.

Action Typical permission/current constraint Evidence to preserve first
Mark duplicate Triage or higher can mark duplicates in organization role model; CLI supports --duplicate-of Canonical issue number/URL and reason
Pin issue Write access; GitHub documents up to three pinned issues Why this item deserves durable visibility
Transfer issue Write access in source and target; repositories must share same user/org owner; open issue; private → public transfer is not allowed Target repository, retained/mapped labels/milestone implications
Lock issue conversation Write/collaborator/owner; reason can be public Moderation reason and last useful context

10. Inspect first: prove hosted work state without mutating it

gh auth status --active --hostname github.com

gh issue list --repo OWNER/REPO --state all --limit 30   --json number,title,state,stateReason,labels,assignees,milestone,url   --jq '.[] | {number,title,state,stateReason,labels:[.labels[].name],assignees:[.assignees[].login],milestone:(.milestone.title // null),url}'

gh label list --repo OWNER/REPO --limit 100   --json name,description,isDefault --jq '.[]'

gh api -H "X-GitHub-Api-Version: 2026-03-10"   "/repos/OWNER/REPO/issues?state=all&per_page=100" --paginate   --jq '.[] | select(.pull_request == null) | {number,title,state,labels:[.labels[].name],milestone:(.milestone.title // null)}'

The API example deliberately filters out pull requests. Collection endpoints are paginated; using per_page=100 and --paginate avoids teaching an automation pattern that silently ignores older work. The explicit API-version header matches the current supported version used by this course generation run.

11. DevOps connection: intake quality is an operational control

Defects, deployment failures, upgrade requests, and operational debt compete for the same engineering attention. A disciplined issue system provides consistent evidence for prioritization, incident follow-up, release planning, and trend analysis. Poor taxonomy creates false metrics; poor templates create expensive back-and-forth; overpowered automation can destroy context. The purpose of triage is therefore to preserve meaning while reducing ambiguity.

12. Lesson summary

GitHub Issues are hosted work records layered around Git—not Git objects. Labels classify, milestones group toward bounded goals, assignees communicate ownership, and organization-defined issue types provide an optional cross-repository classification layer. Templates/forms shape intake; Discussions route non-actionable conversation; triage permissions let maintainers organize work without automatically granting code-push rights.

Knowledge check

Why does cloning a repository not give you its issue database?

What is the key difference between a Markdown issue template and a YAML issue form?

Can a Triage-role collaborator create a new repository label in the standard organization role model?

A user asks an open-ended “how should this project evolve?” question with no committed action. Issue or Discussion?

Why should a duplicate be linked to a canonical issue instead of simply closed with “duplicate”?

Next lesson

Build the intake system safely

Lesson 02 creates a disposable repository, installs a small taxonomy and issue form, exercises the current gh issue commands, and verifies every state change.

Authoritative references

 About issues
 Managing labels
 About milestones
 Configuring issue templates
 Syntax for issue forms
 About discussions
 Moderating discussions
 Repository roles
 Managing issue types
 Marking duplicates
 Transferring an issue
 Locking conversations
 Pinning an issue
 gh issue
 REST API endpoints for issues
 REST API versions

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.