Home DevOps GitHub Issues and Projects — The 'Fix' Issue That Sat for 6 Months
Beginner 4 min · July 12, 2026
GitHub Issues and Projects: Track Work Beyond Code

GitHub Issues and Projects — The 'Fix' Issue That Sat for 6 Months

A critical security issue was filed but never triaged.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Production
production tested
July 12, 2026
last updated
2,406
articles · all by Naren
Before you start⏱ 15 min
  • GitHub account, basic understanding of Git and GitHub repositories, familiarity with Markdown, command-line basics for running scripts (optional).
 ● Production Incident
Quick Answer

Open an issue: gh issue create --title 'Bug' --body 'Description'. Create a Project Board: GitHub → Projects → New Project. Automate: use issue templates, labels, and project workflows.

✦ Definition~90s read
What is GitHub Issues and Projects?

GitHub Issues tracks bugs, features, and tasks. GitHub Projects provides a Kanban-style board to organize and prioritize issues across repositories. Together they form GitHub's built-in project management system.

GitHub Issues is like a communal to-do list for your codebase.
Plain-English First

GitHub Issues is like a communal to-do list for your codebase. Anyone can add a card ('the login button is broken'), assign it to someone, label it ('bug', 'urgent'), and discuss the fix. GitHub Projects is a bulletin board where you move these cards through columns: 'To Do' → 'In Progress' → 'Done'. Together they turn chaos (random bug reports in Slack) into a structured workflow that the whole team can see.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

Every team has a Slack channel where bugs get reported. And lost. GitHub Issues solves this by putting every bug, feature request, and task in a searchable, assignable, labeled database tied directly to the code. GitHub Projects adds prioritization and workflow tracking. The combination is powerful enough for many teams to replace Jira entirely. This guide covers issue templates, labels, project boards with automation, and the incident where a missing triage process led to a 6-month-old security issue becoming a production breach.

Why Track Work Beyond Code?

Git is great for code, but it's terrible for project management. Issues and pull requests don't tell you why a feature was built, who approved it, or whether it's blocked by a dependency. GitHub Issues and Projects fill that gap by providing a structured way to track tasks, bugs, and features alongside your codebase. Without them, teams rely on Slack messages, spreadsheets, or memory—each a recipe for chaos. In production, I've seen teams miss deadlines because a critical bug was discussed in a DM and never logged. GitHub Projects gives you a single source of truth for what's being worked on, why, and by whom. It's not optional; it's how you scale beyond a 2-person team.

.github/ISSUE_TEMPLATE/bug_report.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: ["bug"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!
  - type: input
    id: contact
    attributes:
      label: Contact Details
      description: How can we get in touch if we need more info?
      placeholder: ex. email@example.com
    validations:
      required: false
  - type: textarea
    id: what-happened
    attributes:
      label: What happened?
      description: Also tell us, what did you expect to happen?
      placeholder: Tell us what you see!
    validations:
      required: true
  - type: dropdown
    id: version
    attributes:
      label: Version
      description: What version of our software are you running?
      options:
        - 1.0.2
        - 1.0.3
      default: 0
    validations:
      required: true
  - type: textarea
    id: logs
    attributes:
      label: Relevant log output
      description: Please copy and paste any relevant log output. This will be automatically formatted into code.
      render: shell
Output
Creates a structured bug report form with required fields.
🔥Templates Save Time
Issue templates enforce consistency. Without them, you'll get vague reports like 'it doesn't work' with no steps to reproduce.
📊 Production Insight
In production, untracked work leads to 'shadow work'—tasks that consume time but never appear on a roadmap. This kills predictability.
🎯 Key Takeaway
GitHub Issues and Projects provide a structured way to track work beyond code, preventing chaos in team communication.
github-issues-projects THECODEFORGE.IO GitHub Issues and Projects Integration Stack Layered architecture of issue tracking and project management User Interface Issue Form | Project Board View | Pull Request Configuration Layer Issue Templates | Labels | Milestones Automation Layer Workflows | Actions | Webhooks Data Layer Issues | Projects | Repositories Integration Layer Slack | Jira | CI/CD THECODEFORGE.IO
thecodeforge.io
Github Issues Projects

Setting Up Issue Templates for Consistency

Issue templates are your first line of defense against ambiguity. They force reporters to provide structured information, making triage faster. GitHub supports both Markdown-based templates and YAML-based forms. I prefer YAML forms because they enforce required fields and dropdowns, reducing back-and-forth. For example, a bug report template should include environment details, steps to reproduce, expected vs actual behavior, and logs. In production, I've seen teams waste hours asking 'What version are you on?' because the template didn't require it. Set up templates in .github/ISSUE_TEMPLATE/ and configure them in the repository settings. You can also use config.yml to blank issues and link to a contact channel.

.github/ISSUE_TEMPLATE/config.ymlYAML
1
2
3
4
5
6
7
8
blank_issues_enabled: false
contact_links:
  - name: GitHub Community Support
    url: https://github.com/orgs/community/discussions
    about: Please ask and answer questions here.
  - name: Security Issues
    url: https://github.com/org/security
    about: Please report security vulnerabilities here.
Output
Disables blank issues and directs users to proper channels.
💡Start with Three Templates
Bug report, feature request, and task. That's enough for most teams. Add more only when you see a pattern of missing info.
📊 Production Insight
Without templates, your backlog becomes a graveyard of incomplete reports. Triage becomes a full-time job.
🎯 Key Takeaway
Issue templates enforce consistency and reduce triage time by requiring structured data upfront.

Organizing Work with Labels and Milestones

Labels and milestones are the scaffolding of your project management. Labels categorize issues by type (bug, enhancement), priority (critical, low), or status (needs review, blocked). Milestones group issues toward a specific goal, like a release or sprint. In production, I've seen teams use too many labels (50+) and then ignore them. Stick to a small set: bug, feature, tech-debt, blocked, needs-triage, and priority: critical. Milestones should have a clear due date and description. Use the GitHub API to enforce that issues in a milestone must have a specific label. This prevents 'zombie milestones' that never close.

label-milestone-check.shBASH
1
2
3
4
5
6
7
8
#!/bin/bash
# Check that all issues in a milestone have a priority label
MILESTONE="v1.0"
gh issue list --milestone "$MILESTONE" --json number,labels | jq -r '.[] | select(.labels[].name | startswith("priority:") | not) | .number' | while read num; do
  echo "Issue #$num missing priority label"
  # Optionally add a default label
  # gh issue edit $num --add-label "priority: medium"
done
Output
Lists issues in milestone v1.0 that lack a priority label.
⚠ Label Proliferation
Too many labels create noise. Enforce a maximum of 10 labels per repository. Use automation to clean up unused ones.
📊 Production Insight
I once saw a team with 80 labels. Nobody used them. They ended up ignoring all labels, defeating the purpose.
🎯 Key Takeaway
Labels and milestones provide structure; keep them minimal and enforce consistency with automation.
Issue Templates vs Ad-Hoc Issues Comparing structured and unstructured issue creation Issue Templates Ad-Hoc Issues Consistency Enforces uniform fields and structure Varies per reporter, often incomplete Onboarding Guides new contributors with prompts Requires prior knowledge of project norm Automation Triggers labels, assignments, project li Manual setup needed for each issue Searchability Standard fields enable filtering and que Inconsistent data reduces search effecti Maintenance Requires template updates as project evo No template maintenance, but more cleanu THECODEFORGE.IO
thecodeforge.io
Github Issues Projects

GitHub Projects: Kanban Boards That Sync with Issues

GitHub Projects (the new Projects experience) is a kanban board that syncs with issues and pull requests. Unlike the old Projects, the new version is built on a spreadsheet-like table with custom fields, views, and automation. You can create a board with columns like 'To Do', 'In Progress', 'In Review', and 'Done'. Each card is an issue or PR. The killer feature is automation: when a PR is merged, you can automatically move the linked issue to 'Done'. In production, I've seen teams manually drag cards and forget to update status. Automate everything. Use the 'Item added to project' trigger to set default fields like 'Status: To Do'.

.github/workflows/project-automation.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
name: Project Automation
on:
  issues:
    types: [opened]
jobs:
  add-to-project:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/add-to-project@v0.5.0
        with:
          project-url: https://github.com/orgs/myorg/projects/1
          github-token: ${{ secrets.PROJECT_TOKEN }}
          labeled: bug, feature
          label-operator: OR
Output
Automatically adds new issues with 'bug' or 'feature' labels to project board.
💡Use Views, Not Columns
Instead of multiple columns, use a single 'Status' field and create views (e.g., 'My Tasks', 'Sprint Backlog'). This is more flexible.
📊 Production Insight
Manual board updates are the first thing to break under pressure. Automate status changes from PR merges and issue closures.
🎯 Key Takeaway
GitHub Projects with automation reduces manual tracking and keeps status accurate in real time.

Linking Issues to Pull Requests for Traceability

When a PR closes an issue, GitHub automatically links them and moves the issue to 'Done' if configured. This creates an audit trail: you can see exactly which code change resolved which issue. Use keywords like closes #123, fixes #123, or resolves #123 in the PR description. In production, I've seen teams forget to link issues, leading to orphaned PRs that nobody understands. Enforce linking with a branch naming convention (e.g., feature/123-add-login) and a PR template that asks 'Which issue does this close?'. Use GitHub Actions to block PRs without an issue reference.

.github/workflows/require-issue-link.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name: Require Issue Link
on:
  pull_request:
    types: [opened, edited]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v6
        with:
          script: |
            const pr = context.payload.pull_request;
            const body = pr.body || '';
            const linked = /(close|fix|resolve)s?\s+#\d+/i.test(body);
            if (!linked) {
              core.setFailed('PR must link to an issue using keywords like "closes #123".');
            }
Output
Fails the workflow if PR body doesn't contain an issue-closing keyword.
⚠ Don't Rely on Manual Linking
Developers forget. Automate the check. Also, require branch names to include the issue number for extra traceability.
📊 Production Insight
In a post-incident review, you need to know why a change was made. Without issue links, you're guessing.
🎯 Key Takeaway
Linking issues to PRs creates an audit trail and ensures every code change is justified by a tracked task.

Using Custom Fields for Priority, Effort, and Sprint

GitHub Projects' custom fields let you add metadata like priority (High, Medium, Low), effort (story points), sprint iteration, or target release. These fields power views and filters. For example, you can create a view that shows only 'High priority' items in the current sprint. In production, I've seen teams use labels for priority, but labels are messy for sorting. Custom fields are typed (text, number, date, single select) and can be used in automation. Use a 'Sprint' field with a date range to track iterations. Combine with a 'Points' field for velocity tracking. Export the project data to a CSV for reporting.

export-project.jsJAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Export project items to CSV using GitHub GraphQL API
const { graphql } = require('@octokit/graphql');
const fs = require('fs');

const query = `
  query($owner: String!, $repo: String!, $project: Int!) {
    repository(owner: $owner, name: $repo) {
      projectV2(number: $project) {
        items(first: 100) {
          nodes {
            content {
              ... on Issue {
                title
                number
                state
              }
            }
            fieldValues(first: 10) {
              nodes {
                ... on ProjectV2ItemFieldSingleSelectValue {
                  name
                  field {
                    ... on ProjectV2SingleSelectField {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
`;

// Usage: pass owner, repo, project number
// Outputs CSV to stdout
Output
Exports project items with custom fields to CSV for reporting.
Try it live
🔥Start with Three Custom Fields
Priority, Effort (story points), and Sprint. Add more only when you have a clear need. Too many fields discourage use.
📊 Production Insight
Without effort estimates, you can't measure velocity. Without sprint fields, you can't track iteration progress. Both are essential for planning.
🎯 Key Takeaway
Custom fields add structured metadata that enables filtering, sorting, and reporting beyond basic labels.

Automating Workflows with GitHub Actions and Project Automation

GitHub Actions can automate almost any project management task: auto-assign issues, add labels based on content, move cards between columns, or send Slack notifications. For example, when a PR is merged, you can automatically close the linked issue and move it to 'Done'. Or when an issue is opened with 'bug' label, auto-assign it to the on-call engineer. In production, I've seen teams manually assign issues and forget to update status. Automate everything that's deterministic. Use the actions/add-to-project action to add issues to a project board. Use actions/github-script for custom logic.

.github/workflows/auto-assign-bug.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
name: Auto Assign Bug
on:
  issues:
    types: [opened, labeled]
jobs:
  assign:
    if: contains(github.event.issue.labels.*.name, 'bug')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v6
        with:
          script: |
            const assignees = ['sre-team'];
            await github.rest.issues.addAssignees({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              assignees: assignees
            });
Output
Automatically assigns bug issues to the SRE team.
💡Start Small, Then Expand
Automate one workflow at a time. The first should be 'move issue to Done when PR is merged'. That alone saves hours per week.
📊 Production Insight
Manual processes are the first to break during incidents. Automate status transitions so your board reflects reality even under stress.
🎯 Key Takeaway
Automation reduces manual overhead and ensures consistency in project management workflows.

Reporting and Insights: Measuring Team Velocity

GitHub Projects doesn't have built-in velocity charts, but you can export data and use external tools. Use the GraphQL API to fetch items with custom fields like 'Points' and 'Sprint'. Then calculate velocity as total points completed per sprint. In production, I've seen teams guess velocity and consistently miss commitments. Track actual vs planned points over 3-4 sprints to get a reliable number. Use a simple script to generate a burndown chart. Also track cycle time (time from issue opened to closed) to identify bottlenecks. Share these metrics in a weekly team dashboard.

velocity.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import requests
import json

# Fetch project items via GraphQL
query = """
query($owner: String!, $repo: String!, $project: Int!) {
  repository(owner: $owner, name: $repo) {
    projectV2(number: $project) {
      items(first: 100) {
        nodes {
          content {
            ... on Issue {
              title
              closedAt
              createdAt
            }
          }
          fieldValues(first: 10) {
            nodes {
              ... on ProjectV2ItemFieldNumberValue {
                number
                field {
                  ... on ProjectV2NumberField {
                    name
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
"""

# Calculate velocity: sum of points for issues closed in last sprint
# (pseudo-code, requires actual API call)
print("Velocity: 34 points in last sprint")
print("Cycle time: 4.2 days average")
Output
Prints velocity and cycle time metrics.
🔥Don't Obsess Over Numbers
Velocity is a planning tool, not a performance metric. Use it to forecast, not to evaluate individuals.
📊 Production Insight
Without metrics, you're flying blind. I've seen teams consistently overcommit because they had no historical velocity data.
🎯 Key Takeaway
Export project data to calculate velocity and cycle time for data-driven sprint planning.

Best Practices for Scaling Across Teams

When multiple teams share a repository, use issue templates with team-specific fields, labels per team (e.g., team:platform), and project boards per team or per initiative. Use the 'Iteration' field for sprints and 'Team' custom field for ownership. In production, I've seen cross-team issues fall through cracks because nobody owned them. Use a shared 'Triage' board where unassigned issues live until a team picks them up. Also, use GitHub's 'Organization Projects' to create cross-repo boards for company-wide initiatives. Enforce a policy that every issue must have a team label and a priority.

.github/ISSUE_TEMPLATE/feature_request.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
name: Feature Request
description: Suggest an idea for this project
title: "[Feature]: "
labels: ["feature"]
body:
  - type: dropdown
    id: team
    attributes:
      label: Team
      options:
        - Platform
        - Frontend
        - Backend
        - Data
    validations:
      required: true
  - type: textarea
    id: description
    attributes:
      label: Description
      placeholder: What problem does this solve?
    validations:
      required: true
Output
Feature request template with team dropdown.
⚠ Avoid Over-Engineering
Start with a single board per repository. Only split into team boards when you have >10 people or clear ownership conflicts.
📊 Production Insight
In a large org, I've seen issues sit for weeks because nobody knew which team owned them. A triage board with a rotating owner fixes that.
🎯 Key Takeaway
Scale by using team labels, separate boards, and triage processes to prevent cross-team issues from being ignored.

Common Pitfalls and How to Avoid Them

Pitfall 1: Over-customization. Too many fields, labels, and workflows confuse the team. Start minimal and iterate. Pitfall 2: Ignoring the board. If the board doesn't reflect reality, people stop using it. Automate status updates and enforce discipline. Pitfall 3: Using issues for everything. Not every conversation needs an issue. Use discussions for open-ended questions. Pitfall 4: No cleanup. Close stale issues after 90 days of inactivity. Use a GitHub Action to auto-close issues with a 'stale' label. In production, I've seen backlogs with thousands of open issues, most of which are irrelevant. Regular grooming is essential.

.github/workflows/stale.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
name: Close Stale Issues
on:
  schedule:
    - cron: '0 0 * * *'
jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v8
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          stale-issue-message: 'This issue is stale. Please comment if still relevant.'
          days-before-stale: 90
          days-before-close: 14
          stale-issue-label: 'stale'
          exempt-issue-labels: 'pinned,security'
Output
Marks issues as stale after 90 days, closes after 14 more days.
⚠ Don't Auto-Close Everything
Exempt critical labels like 'security' or 'blocker'. Also, notify the team before enabling auto-close to avoid losing important items.
📊 Production Insight
A backlog of 2000+ issues is a sign of process failure. Most are noise. Groom ruthlessly or your team will ignore the board entirely.
🎯 Key Takeaway
Avoid over-customization, keep the board accurate, and regularly clean up stale issues to maintain a healthy backlog.
● Production incidentPOST-MORTEMseverity: high

The Security Issue That Sat in the Backlog for 6 Months

Symptom
A security researcher filed a critical XSS vulnerability via GitHub Issues. The issue had no assignee, no label, no priority. It sat in the default 'Issues' list for 6 months. An attacker exploited the same vulnerability. The breach was traced to the unfiled issue.
Assumption
The team assumed 'critical' issues would naturally get attention. They had no triage process, no labels, no project board.
Root cause
1. A security researcher opened an issue describing an XSS vulnerability with a proof of concept. 2. No one was assigned (GitHub doesn't auto-assign). 3. No label was added (no label convention existed). 4. The issue fell off the first page of the issue list within a week. 5. 6 months later, the vulnerability was exploited in production. 6. During the postmortem, the 6-month-old issue was found — with all the information needed to prevent the breach.
Fix
1. Fixed the XSS vulnerability immediately. 2. Created issue templates with required fields: severity, steps to reproduce, affected versions. 3. Set up label automation: issues filed via security template get 'security' label automatically. 4. Created a 'Security Triage' project board with auto-add from the security label. 5. Assigned a rotating security reviewer to triage the board weekly. 6. Configured GitHub's 'code security' alerts to auto-file issues for dependency vulnerabilities.
Key lesson
  • Every issue needs a triage process: labels, project board, assignee rotation.
  • Security issues need a separate triage pipeline with SLA.
  • Use issue templates with required fields.
  • Automate labeling and project board placement.
Production debug guideUse this when production is on fire3 entries
Symptom · 01
Need to report a bug
Fix
Open an issue with a clear title, steps to reproduce, expected vs actual behavior
Symptom · 02
Need to organize issues into a workflow
Fix
Create a GitHub Project with columns: Backlog, To Do, In Progress, Review, Done
Symptom · 03
Need to automate issue triage
Fix
Use issue templates, label automation rules, and project board workflows
⚙ Quick Reference
10 commands from this guide
FileCommand / CodePurpose
.githubISSUE_TEMPLATEbug_report.yamlname: Bug ReportWhy Track Work Beyond Code?
.githubISSUE_TEMPLATEconfig.ymlblank_issues_enabled: falseSetting Up Issue Templates for Consistency
label-milestone-check.shMILESTONE="v1.0"Organizing Work with Labels and Milestones
.githubworkflowsproject-automation.ymlname: Project AutomationGitHub Projects
.githubworkflowsrequire-issue-link.ymlname: Require Issue LinkLinking Issues to Pull Requests for Traceability
export-project.jsconst { graphql } = require('@octokit/graphql');Using Custom Fields for Priority, Effort, and Sprint
.githubworkflowsauto-assign-bug.ymlname: Auto Assign BugAutomating Workflows with GitHub Actions and Project Automat
velocity.pyquery = """Reporting and Insights
.githubISSUE_TEMPLATEfeature_request.yamlname: Feature RequestBest Practices for Scaling Across Teams
.githubworkflowsstale.ymlname: Close Stale IssuesCommon Pitfalls and How to Avoid Them

Key takeaways

1
Issue templates enforce consistent bug reports
2
Labels + project boards enable structured triage
3
Automation
auto-label, auto-assign, auto-move between columns
4
Security issues need their own triage pipeline with SLA
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the difference between GitHub Issues and GitHub Projects?
02
How do I link a pull request to an issue?
03
Can I automate moving issues between columns in GitHub Projects?
04
How do I set up issue templates for my repository?
05
What are custom fields in GitHub Projects and how do I use them?
06
How can I measure team velocity using GitHub Projects?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 12, 2026
last updated
2,406
articles · all by Naren
🔥

That's Git. Mark it forged?

4 min read · try the examples if you haven't

Previous
Git LFS: Large File Storage for Git Repositories
34 / 47 · Git
Next
GitHub Pages: Host Static Sites from Your Repository