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.
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.ymlparticipates 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
ghoutput, and versioned REST responses before mutation.
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.
.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
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.
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?
Issues are GitHub-hosted records, not Git objects stored in the repository object database.
What is the key difference between a Markdown issue template and a YAML issue form?
A Markdown template pre-fills guidance, while a YAML issue form renders structured fields and can require specific responses/checkboxes; forms are currently public preview.
Can a Triage-role collaborator create a new repository label in the standard organization role model?
No. Triage can apply/dismiss existing labels, while creating/editing/deleting labels requires Write or higher.
A user asks an open-ended “how should this project evolve?” question with no committed action. Issue or Discussion?
Usually Discussion, because it is exploratory conversation. Convert a resulting decision into an issue when concrete tracked work emerges.
Why should a duplicate be linked to a canonical issue instead of simply closed with “duplicate”?
The canonical link preserves context, lets future readers find the active record, and creates auditable evidence for why work was closed.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.