Skip to content
Pipelines and Pizza 🍕
Go back

PR Templates and Issue Templates

12 min read

Last month I opened a pull request from a teammate that had a single line in the description: “fixed stuff.” That was it. No context, no link to the ticket, no explanation of what changed or why. I had to read every file diff, and message them on Teams just to understand what I was reviewing.

The week before that, someone filed a bug report with “it’s broken” as the entire issue body. No environment info, no reproduction steps, no error messages.

These aren’t bad engineers. They’re busy engineers who didn’t have a structure to follow. The fix is dead simple: templates.


Why Templates Matter

Templates solve the “blank text box” problem. When someone opens a new PR or issue and sees an empty description field, the path of least resistance is to type something quick and move on. But when they see a structured form with specific prompts, they fill it in. Every time.

For infrastructure and DevOps teams, this is especially important. A Terraform PR without a plan output summary is a review bottleneck. A bug report without the environment and provider versions is a guessing game. Templates turn tribal knowledge into repeatable process.

What you get from good templates:

  • Consistent, reviewable PRs that include the context reviewers need
  • Bug reports with reproduction steps from day one
  • Feature requests that capture the “why” alongside the “what”
  • Faster onboarding — new engineers see exactly what’s expected

The .github Directory

Everything lives in the .github/ directory at the root of your repository. Here’s what a well-organized setup looks like:

.github/
├── PULL_REQUEST_TEMPLATE.md              # Default PR template
├── PULL_REQUEST_TEMPLATE/                # Multiple PR templates (optional)
│   ├── infrastructure.md
│   └── hotfix.md
├── ISSUE_TEMPLATE/
│   ├── bug-report.yml                    # YAML form (structured)
│   ├── feature-request.yml               # YAML form (structured)
│   ├── documentation.md                  # Markdown template
│   └── config.yml                        # Template chooser config
└── workflows/
    └── ...                               # GitHub Actions (separate topic)

This directory must exist on your default branch (usually main). GitHub reads templates from there when someone opens a new PR or issue.


Pull Request Templates

The Default Template

Create a file at .github/PULL_REQUEST_TEMPLATE.md and its content automatically populates the description field for every new PR. No configuration needed.

Here’s a solid general-purpose template:

## Summary

<!-- What does this PR do? Why is it needed? -->

## Related Issues

<!-- Link to relevant issues: Fixes #123, Relates to #456 -->

## Changes

- [ ] Change 1
- [ ] Change 2

## Testing

<!-- How did you verify this works? -->

- [ ] Unit tests pass
- [ ] Manual testing completed
- [ ] CI pipeline passes

## Checklist

- [ ] Code follows project conventions
- [ ] Documentation updated (if applicable)
- [ ] No secrets or credentials committed

An Infrastructure-Focused PR Template

For DevOps and infrastructure repos, you need more. Here’s what I use for Terraform projects:

## Summary

<!-- Describe the infrastructure change and business reason -->

## Terraform Plan Output

<details>
<summary>Click to expand plan output</summary>

Paste terraform plan output here


</details>

## Change Type

- [ ] New resource(s)
- [ ] Modified resource(s)
- [ ] Destroyed resource(s)
- [ ] Module update
- [ ] Provider update

## Blast Radius

<!-- What systems/environments are affected? -->

- **Environment:** <!-- dev / staging / production -->
- **Resources affected:** <!-- count from plan -->
- **Downtime expected:** <!-- yes/no, duration -->

## Security Review

- [ ] No IAM policy changes (or reviewed and justified below)
- [ ] No security group / firewall rule changes (or reviewed below)
- [ ] No public endpoints exposed
- [ ] Secrets managed through vault/key store (not hardcoded)
- [ ] `checkov` / `tfsec` scan clean

## Rollback Plan

<!-- How do we undo this if it goes wrong? -->

## Checklist

- [ ] `terraform fmt` applied
- [ ] `terraform validate` passes
- [ ] Plan reviewed -- no unexpected destroys
- [ ] State file not modified manually

That <details> block is a lifesaver. Terraform plans can be hundreds of lines — collapsing them keeps the PR readable while keeping the full output available.

Multiple PR Templates

GitHub supports multiple templates through the .github/PULL_REQUEST_TEMPLATE/ directory. Place your templates there and reference them with a URL query parameter:

https://github.com/OWNER/REPO/compare/main...feature-branch?template=infrastructure.md

The ?template=infrastructure.md parameter tells GitHub which template to load. This is useful for monorepos where application code and infrastructure live side by side and need different review checklists.

One important note: if you use the PULL_REQUEST_TEMPLATE/ subdirectory, GitHub won’t auto-populate any template by default. You lose the automatic behavior of the single-file approach. Most teams are better off with one good default template unless they have a genuine need for multiple.


Issue Templates: Markdown vs YAML Forms

GitHub supports two formats for issue templates, and the difference matters.

FeatureMarkdown (.md)YAML Forms (.yml)
Pre-fills issue bodyYesNo — renders as a web form
Required fieldsNo enforcementYes, with validation
Dropdowns / checkboxesManual text onlyNative form elements
Auto-assign labelsVia frontmatterVia frontmatter
User experienceText editorStructured form
Supported onGitHub, GitHub EnterpriseGitHub, GitHub Enterprise

My recommendation: use YAML forms for bug reports and feature requests (where you need structured data), and markdown templates for things like documentation requests or discussion-style issues.


YAML Issue Forms

YAML forms turn your issue template into a proper web form with dropdowns, text fields, checkboxes, and required field validation. No more “it’s broken” issues.

Bug Report Form

.github/ISSUE_TEMPLATE/bug-report.yml:

name: Bug Report
description: Report a bug or unexpected behavior
title: "[Bug]: "
labels: ["bug", "triage"]
assignees:
  - rjbennett5
body:
  - type: markdown
    attributes:
      value: |
        Thanks for reporting a bug. Please fill out the sections below
        so we can reproduce and fix the issue quickly.

  - type: input
    id: version
    attributes:
      label: Version
      description: What version are you running?
      placeholder: "e.g., v1.2.3 or commit SHA"
    validations:
      required: true

  - type: dropdown
    id: environment
    attributes:
      label: Environment
      description: Where did this happen?
      options:
        - Development
        - Staging
        - Production
    validations:
      required: true

  - type: textarea
    id: description
    attributes:
      label: What happened?
      description: Describe the bug clearly.
      placeholder: "When I run terraform apply, the module fails with..."
    validations:
      required: true

  - type: textarea
    id: expected
    attributes:
      label: Expected behavior
      description: What did you expect to happen instead?
    validations:
      required: true

  - type: textarea
    id: reproduce
    attributes:
      label: Steps to reproduce
      description: Walk us through how to trigger this bug.
      value: |
        1.
        2.
        3.
    validations:
      required: true

  - type: textarea
    id: logs
    attributes:
      label: Relevant logs or error output
      description: Paste any relevant log output here.
      render: shell

  - type: checkboxes
    id: checklist
    attributes:
      label: Before submitting
      options:
        - label: I searched existing issues and this is not a duplicate
          required: true
        - label: I am using the latest version

When a user opens a new issue and selects “Bug Report,” they see a clean web form instead of a blank text area. The render: shell attribute on the logs field automatically wraps the input in a code block — no more forgetting to use triple backticks.

Feature Request Form

.github/ISSUE_TEMPLATE/feature-request.yml:

name: Feature Request
description: Suggest a new feature or improvement
title: "[Feature]: "
labels: ["enhancement"]
body:
  - type: textarea
    id: problem
    attributes:
      label: Problem statement
      description: What problem does this feature solve?
      placeholder: "I frequently need to... but currently..."
    validations:
      required: true

  - type: textarea
    id: solution
    attributes:
      label: Proposed solution
      description: How would you like this to work?
    validations:
      required: true

  - type: textarea
    id: alternatives
    attributes:
      label: Alternatives considered
      description: What workarounds or alternatives have you tried?

  - type: dropdown
    id: priority
    attributes:
      label: Priority
      options:
        - Nice to have
        - Important
        - Critical -- blocking work
    validations:
      required: true

The Template Chooser and config.yml

When you have multiple issue templates, GitHub shows a “template chooser” page instead of dumping users straight into a blank issue. You can customize this with .github/ISSUE_TEMPLATE/config.yml:

blank_issues_enabled: false
contact_links:
  - name: Security Vulnerability
    url: https://example.com/security
    about: Please report security issues privately, not as a public issue.
  - name: General Questions
    url: https://github.com/OWNER/REPO/discussions
    about: Ask questions in Discussions instead of opening an issue.

Setting blank_issues_enabled: false forces users to pick a template. No more “it’s broken” drive-bys. The contact_links section adds external links to the chooser page, which is perfect for redirecting security reports or general questions to the right place.


Markdown Issue Templates

For less structured issues, a classic markdown template still works well. It pre-fills the issue body with your section prompts, and the user edits from there.

.github/ISSUE_TEMPLATE/documentation.md:

---
name: Documentation Improvement
about: Suggest an update or fix for documentation
title: "[Docs]: "
labels: documentation
assignees: ""
---

## What page or section needs updating?

## What's wrong or missing?

## Suggested improvement

The YAML frontmatter block (between the --- lines) is required for markdown issue templates. It sets the template name, description, default title prefix, labels, and assignees that appear in the template chooser. Without this frontmatter, GitHub won’t recognize the file as a template.


Hands-On Lab: Add Templates to Your Repo

Let’s set up a complete template system from scratch. You can follow along in any existing repository.

Step 1: Create the directory structure

mkdir -p .github/ISSUE_TEMPLATE

Step 2: Add a PR template

Create .github/PULL_REQUEST_TEMPLATE.md:

## Summary

<!-- What does this PR do and why? -->

## Related Issues

<!-- Fixes #, Relates to # -->

## Changes

-

## Testing

- [ ] Tests pass locally
- [ ] CI pipeline passes

## Checklist

- [ ] Code follows project conventions
- [ ] No secrets committed
- [ ] Documentation updated (if needed)

Step 3: Add a bug report issue form

Create .github/ISSUE_TEMPLATE/bug-report.yml:

name: Bug Report
description: Report a bug
title: "[Bug]: "
labels: ["bug", "triage"]
body:
  - type: input
    id: version
    attributes:
      label: Version
      placeholder: "v1.0.0"
    validations:
      required: true
  - type: textarea
    id: description
    attributes:
      label: What happened?
    validations:
      required: true
  - type: textarea
    id: steps
    attributes:
      label: Steps to reproduce
      value: |
        1.
        2.
        3.
    validations:
      required: true
  - type: textarea
    id: logs
    attributes:
      label: Logs or error output
      render: shell

Step 4: Add a feature request form

Create .github/ISSUE_TEMPLATE/feature-request.yml:

name: Feature Request
description: Suggest an improvement
title: "[Feature]: "
labels: ["enhancement"]
body:
  - type: textarea
    id: problem
    attributes:
      label: Problem statement
    validations:
      required: true
  - type: textarea
    id: solution
    attributes:
      label: Proposed solution
    validations:
      required: true

Step 5: Configure the template chooser

Create .github/ISSUE_TEMPLATE/config.yml:

blank_issues_enabled: false
contact_links:
  - name: Questions & Discussion
    url: https://github.com/OWNER/REPO/discussions
    about: Use Discussions for questions, not Issues.

Step 6: Commit and push

git add .github/
git commit -m "feat: add PR and issue templates"
git push

Once this hits your default branch, every new PR and issue will use your templates automatically. Open a test PR or navigate to the “New Issue” page to verify everything renders correctly.


Troubleshooting Guide

ProblemCauseFix
PR template not loadingFile not on default branchMerge to main first
PR template not loadingWrong filenameMust be exactly PULL_REQUEST_TEMPLATE.md (case insensitive)
Issue form validation errorDuplicate dropdown optionsEach option in a dropdown must be unique
Issue form not appearingFile extension issueUse .yml — both .yml and .yaml work, but stay consistent
Template chooser not showingOnly one template existsChooser appears with 2+ templates or a config.yml
Labels not appliedLabels don’t exist in repoCreate the labels in your repo first — templates reference existing labels
Blank issues still possibleconfig.yml missingAdd blank_issues_enabled: false to config.yml
Multiple PR templates not workingQuery param missingUse ?template=filename.md in the PR URL

Frequently Asked Questions

Can I use YAML forms for PR templates? No. YAML forms are only supported for issue templates. PR templates must be markdown files.

Do templates work on private repos? Yes. Templates work on all GitHub repository types — public, private, and internal.

Can I set a default assignee on PR templates? Not through the template file itself. Use a GitHub Action or a CODEOWNERS file for automatic PR assignment instead.

What happens if someone removes the template text? Nothing stops them. Templates are a nudge, not a gate. If you need enforcement, use a GitHub Action that checks the PR body against required sections and blocks the merge if they’re missing.

Do templates work with the GitHub CLI? Yes. When you create a PR with gh pr create, it opens your editor with the default template pre-filled. For issues, gh issue create --template "Bug Report" selects a specific template.

Can I combine a single default PR template with a PULL_REQUEST_TEMPLATE/ directory? No. If the directory exists, GitHub ignores the single file. Pick one approach.


Quick Reference

# Directory structure
.github/PULL_REQUEST_TEMPLATE.md           # Single default PR template
.github/PULL_REQUEST_TEMPLATE/name.md      # Multiple PR templates
.github/ISSUE_TEMPLATE/name.yml            # YAML issue form
.github/ISSUE_TEMPLATE/name.md             # Markdown issue template
.github/ISSUE_TEMPLATE/config.yml          # Template chooser config

# Use a specific PR template via URL
?template=hotfix.md

# Create issue with specific template via CLI
gh issue create --template "Bug Report"

# Create PR (auto-loads default template)
gh pr create

What’s Next

Next post: Git Tags and Release Management. We’ll cover how to tag releases properly, the difference between lightweight and annotated tags, semantic versioning workflows, and how to automate release notes from your commit history.

Happy automating!