Skip to content
ExponaBLOG
GIT & GITHUB FOR PROFESSIONAL DEVELOPERS · DAY 059 / 100

Issue templates and labels: patterns, pitfalls and fixes

Organize work on GitHub.

Expona Admin7 min read

Introduction

By day 59, you've mastered commits, branches, and pull requests. Now it's time to organize the work itself. Issue templates and labels are the scaffolding that turns a GitHub repository into a predictable, self-documenting workspace where every team member knows what to expect and what's expected of them. In this guide, we'll explore issue templates and labels: patterns, pitfalls and fixes—the unglamorous but essential infrastructure that separates chaotic backlogs from managed ones.

Why issue templates matter

A well-designed issue template is like a contract between the reporter and the team. It ensures that bugs come with reproducible steps, features arrive with clear acceptance criteria, and nothing gets lost in translation.

Without templates, you'll see issues like:

  • "The thing is broken"
  • "Can we make it faster?"
  • "Check this out" with a screenshot but no context

With templates, contributors fill in structured fields before they even write their title. Let's look at a practical bug template:

bash
name: Bug Report
description: Report a reproducible defect
title: "[Bug] "
labels: ["bug", "needs-triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for reporting. Please fill in all fields below.
  - type: input
    id: environment
    attributes:
      label: Environment
      description: Node version, OS, browser if relevant
      placeholder: "Node 18.12.0, Ubuntu 22.04"
    validations:
      required: true
  - type: textarea
    id: steps
    attributes:
      label: Steps to reproduce
      description: Exact sequence to trigger the issue
      placeholder: |
        1. Run `npm install`
        2. Call POST /api/users with body {...}
        3. Observe the error
    validations:
      required: true
  - type: textarea
    id: actual
    attributes:
      label: Actual behavior
      description: What happened?
      placeholder: "Returns 500 with no message"
    validations:
      required: true
  - type: textarea
    id: expected
    attributes:
      label: Expected behavior
      description: What should happen?
      placeholder: "Returns 400 with validation error details"
    validations:
      required: true
  - type: textarea
    id: logs
    attributes:
      label: Logs or error output
      description: Paste full stack trace or relevant logs
      render: shell

This template enforces rigor. A reporter must think through their issue before submitting. When your team triages the issue later, you have everything you need.

Feature request templates

Feature requests are different from bugs. They need context, motivation, and success criteria—not reproduction steps.

bash
name: Feature Request
description: Propose a new capability
title: "[Feature] "
labels: ["enhancement"]
body:
  - type: textarea
    id: problem
    attributes:
      label: Problem or use case
      description: What problem does this solve? Who needs it?
      placeholder: "Users in the EU need GDPR-compliant data export"
    validations:
      required: true
  - type: textarea
    id: solution
    attributes:
      label: Proposed solution
      description: How should it work?
      placeholder: "Add a /api/users/:id/export endpoint returning JSON"
    validations:
      required: true
  - type: textarea
    id: alternatives
    attributes:
      label: Alternatives considered
      description: What other approaches did you think of?
      placeholder: "CSV export, but JSON is more programmatic"
  - type: textarea
    id: acceptance
    attributes:
      label: Acceptance criteria
      description: How do we know it's done?
      placeholder: |
        - Endpoint accepts user ID and format parameter
        - Returns all user data in requested format
        - Includes audit log entry
  - type: dropdown
    id: impact
    attributes:
      label: Estimated impact
      description: How many users benefit?
      options:
        - "Many (100s or 1000s)"
        - "Some (10s-100s)"
        - "Few (1-10)"
    validations:
      required: true

The feature template asks for why before how. This prevents the team from building the wrong thing.

Labels for type, priority, and area

Labels are the vocabulary of your backlog. Without them, finding "all critical frontend bugs" requires scrolling and memory. With them, a single filter reveals exactly what you need.

A sensible label strategy includes three dimensions:

Type labels

  • bug — defect in production behavior
  • enhancement — new feature or improvement
  • documentation — docs, README, comments
  • chore — maintenance, refactoring, no user impact
  • question — clarification needed, not a real issue

Priority labels

  • p0-critical — production outage, data loss risk, security breach
  • p1-high — significant user impact, broken feature
  • p2-medium — noticeable but workaround exists
  • p3-low — nice-to-have, cosmetic, future consideration

Area labels

  • area/auth
  • area/api
  • area/ui
  • area/database
  • area/devops

You'll also want operational labels:

  • needs-triage — new, not yet reviewed
  • blocked — waiting on another issue or external event
  • duplicate — same as another issue
  • help-wanted — good for first contributors
  • stale — inactive for 90+ days, candidate for closure

The key is restraint. Fifteen labels per repository is a good range. Beyond that, labeling becomes tedious and inconsistent.

Milestones for releases and planning

Milestones let you group issues by release or sprint. They're simpler than labels but equally powerful for planning.

Create a milestone named v2.5.0 or Sprint 12. Add issues to it. GitHub shows you a progress bar: "18 of 24 issues closed." You can see what's on track and what's slipping.

A practical pattern: create a milestone per quarter or release cycle, and add issues during sprint planning. When you close an issue, you check off progress automatically.

Milestone Purpose Typical timeline
v2.5.0 (Open) Current sprint or release in progress 2–4 weeks
v2.6.0 (Open) Next planned release 4–8 weeks out
Backlog (Open) Important but unscheduled work No deadline
v2.4.0 (Closed) Historical releases, for reference Archived

Don't create too many open milestones. Two to three active ones keep the roadmap clear. Older milestones can be closed as their releases ship.

Closing stale issues politely

Over time, issues accumulate. Some are genuinely abandoned. Others are fixed but never closed. Many are waiting for feedback that never comes.

A stale issue is a tax on morale. It signals that the team doesn't care, or that the process has broken down.

GitHub Actions can automate this. Here's a workflow that marks issues as stale after 90 days of inactivity, then closes them 14 days later:

bash
name: Close stale issues
on:
  schedule:
    - cron: '0 0 * * 0'  # Weekly on Sunday

jobs:
  stale:
    runs-on: ubuntu-latest
    permissions:
      issues: write
    steps:
      - uses: actions/stale@v9
        with:
          days-before-stale: 90
          days-before-close: 14
          stale-issue-label: 'stale'
          stale-issue-message: |
            This issue has been inactive for 90 days. 
            If you'd like us to reopen it, please comment or react with a thumbs-up.
            It will be closed in 14 days unless activity resumes.
          close-issue-message: |
            Closing due to inactivity. Feel free to reopen if this is still relevant.
          exempt-issue-labels: 'pinned,blocked'
          operations-per-run: 100

The tone matters. Be respectful. Explain what's happening and how to get the issue reopened. Many reporters will re-engage if you ask politely.

Common mistakes

Over-labeling: Some teams create fifty labels with overlapping semantics. No one remembers which is which, and consistency suffers. Start with ten and add only when you feel real pain.

Empty templates: A template that's too permissive becomes useless. If every field is optional, people skip them. Make required fields non-negotiable, but don't ask for information you won't use.

Ignoring the backlog: Milestones and labels only work if someone triages incoming issues within 24 hours. Without triage, your backlog becomes a junk drawer. Assign rotation responsibility.

Closing without context: Automated stale-issue closure can feel cold. Add a human-readable message explaining why and how to reopen. Even better, let a human review before closing.

Orphaned milestones: Old milestones collect issues but are never closed. Clean them up quarterly. Move active work to new milestones, and archive the old ones.

Key takeaways

  • Issue templates enforce structure and reduce triage time. Create separate templates for bugs and features, with required fields that capture what you actually need.
  • Labels should span three dimensions: type (bug, enhancement, docs), priority (p0–p3), and area (auth, api, ui). Keep the total count under twenty.
  • Milestones group issues by release or sprint. Maintain two to three active milestones and close old ones when their release ships.
  • Stale issues are momentum killers. Use GitHub Actions to flag inactive issues after 90 days and close them after 14 more, but always include a polite message and a path to reopen.
  • Triage is a daily habit, not a one-time event. Assign responsibility and protect time for it.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles

Issue templates and labels | Git & GitHub | Expona