Feedback loop

Feature request template that becomes a changelog entry

6 min read

A feature request template is a form with four questions: what the person is trying to do, what stops them, what they tried instead, and how they want to be told when it is done. Everything else that usually appears on one, priority pickers, effort estimates, business value scores, is for the team receiving the request, and it is filled in wrongly by the person sending it.

Tidy requests are the wrong test for a template. The right one: six months later, when the feature ships, can someone find the request, understand it, and tell the person who wrote it? Most templates are designed for intake. This one is designed for the day the loop closes.

What should a feature request template include?

It should include the goal, the blocker, the workaround, and a way back to the requester. Four fields, in that order, each answering a question the team will ask later.

FieldThe question it answers laterWhy it is on the form
What are you trying to do?Is the feature we built the one they needed?The goal outlives any specific proposal
What stops you today?What does “done” look like?Names the gap without prescribing the fix
What do you do instead?How urgent is this, really?A painful workaround is a stronger signal than a priority picker
How should we tell you?Who gets the “shipped” message?This is the field most templates leave off

What is deliberately absent: a proposed solution as a required field (welcome as a comment, wrong as the framing), a priority picker (every submitter picks high), and any estimate of effort or value (the team’s job, after triage). A template that asks for a solution gets requests for buttons; a template that asks for a goal gets requests for outcomes, and outcomes are what a changelog entry is written about.

The template

This is the GitHub issue template we use, as a form. Paste it into .github/ISSUE_TEMPLATE/feature_request.yml and it renders as a structured form on the New Issue page. Requests filed through it land as issues with the same fields as the ones filed from a feedback widget, which matters for the section after this one.

name: Feature request
description: What you are trying to do, and what stops you.
labels: ["feature-request"]
body:
  - type: textarea
    id: goal
    attributes:
      label: What are you trying to do?
      description: >-
        The outcome, not the button. "Export a month of invoices as one
        PDF" beats "add a PDF export".
    validations:
      required: true
  - type: textarea
    id: blocker
    attributes:
      label: What stops you today?
      description: >-
        Where the product runs out. An error, a missing option, a limit.
    validations:
      required: true
  - type: textarea
    id: workaround
    attributes:
      label: What do you do instead?
      description: >-
        The spreadsheet, the script, the manual step. "Nothing, I gave
        up" is a valid answer.
  - type: input
    id: contact
    attributes:
      label: How should we tell you when it ships?
      description: >-
        An email address, or leave blank to be notified only on this
        issue.

Two details do the work. labels: ["feature-request"] means the request is classified at creation rather than waiting for someone to triage it. And the last field exists because “we will let you know” is a promise, and a promise needs an address.

Which labels should a feature request carry?

A feature request should carry one label for what it is, one for how urgent it is, and one for where it came from. Three labels, three axes, and each one is used by a different reader.

LabelValuesWho reads it
Kindfeature-request, bugThe person deciding which queue it joins
Prioritypriority:low, priority:medium, priority:highThe person planning the next cycle
Sourcefrom-widget, from-form, from-supportThe person measuring where requests come from

The widget applies the first two axes and from-widget when it files a submission as an issue; from-form and from-support are suggestions for requests that arrive other ways. The widget’s labels are a kind (bug or feature-request, decided by a classifier from the message alone), a priority (a calm, specific crash report is high; a duplicate of something already asked is low; anything that even hints at a security problem is bug and high, whatever the wording), and from-widget. The same three axes work for requests that arrive by hand through the template above, which is the point: a request is a request, wherever it entered.

One more convention: the widget strips the submitter’s email address out of the issue body before filing it, because the issue is in a repository that may be public, and replaces it with a submission reference. The address stays out of the issue; the submitter follows the outcome in the widget itself. Do the same with the contact field if your tracker is visible to anyone outside the team.

How does a feature request become a changelog entry?

A feature request becomes a changelog entry when a pull request closes the issue and the entry drafted from that pull request links back to it. The mechanism is GitHub’s own closing keywords: a PR whose description says Fixes #142 closes issue 142 on merge. If your changelog entries are drafted from merged pull requests, the draft can carry the issue number with it, and the entry knows who asked.

That is the reason the template asks for the goal rather than the solution. When the entry is written, the goal is the sentence the writer needs: “You can now export a month of invoices as one PDF” is a changelog entry. “Added PDF export” is a commit message. The changelog tools that draft from pull requests can do the collection and the linking; the wording still needs a person, and the person needs the goal.

What happens when it ships?

The requester gets told, with a link to the entry. In our setup that is automatic for requests that came in through the widget: a comment reading “Shipped — ” with a link to the published entry, posted on the issue once a human has approved the entry, while the widget shows the submitter the same entry. An issue filed by hand from this template gets no automatic comment; close that loop yourself, by the same rule. The comment is posted at approval rather than at merge on purpose: a comment saying something is live before it is live is a broken promise with a timestamp. Each request is notified at most once; a second approval of the same entry does not produce a second comment.

If you do this by hand, the rule is the same. Do not close the loop from the pull request. Close it from the published entry, and close it once. The feed and widget carry the same entry to everyone who did not ask, which is most people; the comment is for the one who did.

Why most feature request templates fail

They are designed to make triage easier and they succeed, at the cost of the only moment that matters to the requester. A template with twelve fields gets fewer requests, and the ones it gets are from people with the patience to fill in twelve fields, which is not the same population as the people who need the feature. A template with four fields, one of which is “how do we reach you”, gets more requests and can honour every one of them.

FAQ

Should a feature request template ask for priority? No. Ask for the workaround instead. “I export to a spreadsheet and re-key it every Friday” tells you more about priority than a dropdown the submitter set to high.

Should requesters propose a solution? They can, in the free text. Do not make it the framing. Requests written as solutions are harder to merge with each other and harder to write a changelog entry about.

Should feature requests appear on a public roadmap? Once they are planned, yes: a label on the same issue puts it in the planned column, and the requester can watch it move. The public roadmap article is the mechanism.

How do I handle duplicates? Link the new request to the existing issue and label it low priority; do not close it. Each duplicate is another person to tell when it ships. With Changeloop’s automatic comment, that person is told only if the pull request also names their issue (Fixes #142, fixes #187).

Where should the template live? In the repository that will receive the pull request, so the closing keyword works. A request in a separate tracker has to be linked by hand at merge time, and that step is the one that gets skipped.


The technical claims in this article have not been independently reviewed. If something here is wrong, tell us and we will correct it.

Related on changeloop: Developer docs, Changelog tools compared

changeloop
The team building a closed-loop changelog. Your users ask, your team ships, the person who asked gets told.