A feedback channel is easy to launch: add an email address, enable GitHub Issues, or link a form.
The difficult part starts after the first message arrives.
Without an explicit workflow, maintainers end up checking several disconnected inboxes. Reporters do not know where to ask questions, private messages cannot be searched by other contributors, and issues sit in an ambiguous state until everyone assumes somebody else handled them.
The solution is not another notification destination. It is a small routing and triage system with one visible entry point, clear ownership, and a limited set of states.
This tutorial builds that system around GitHub, but the same model works with GitLab, a ticket tracker, or a shared support queue.
Start with a routing contract
Before configuring tools, decide where each kind of message belongs.
A practical routing table might look like this:
Message type Destination Visibility Reproducible bug GitHub Issue Public Feature proposal GitHub Issue or Discussion Public Usage question GitHub Discussion Public Security vulnerability Security policy instructions Private Account, billing, or personal data Contact channel Private General feedback that can become actionable Contact channel, then promoted to an issue with permission Private firstThis separation matters. Public channels make reusable knowledge searchable, while private channels provide an escape hatch for information that should not be posted in an issue.
Document the contract in SUPPORT.md:
# Support
## Bugs and feature requests
Open a GitHub issue. Search existing issues first and include a minimal reproduction when reporting a bug.
## Questions
Use GitHub Discussions for setup help and open-ended questions.
## Private messages
Use our private contact page for account details, personal information, or feedback you do not want to post publicly.
## Security vulnerabilities
Do not open a public issue. Follow the instructions in SECURITY.md.
## Review cadence
New issues are reviewed on Tuesdays and Fridays. This is a review target, not a guaranteed resolution time.
Enter fullscreen mode Exit fullscreen mode
Link this file from the README so contributors do not need to discover it by browsing the repository.
## Help and feedback
- [Report a bug](https://github.com/OWNER/REPOSITORY/issues/new/choose)
- [Ask a question](https://github.com/OWNER/REPOSITORY/discussions)
- [Send a private message](https://example.com/contact)
- [Read the support policy](SUPPORT.md)
- [Report a security issue](SECURITY.md)
Enter fullscreen mode Exit fullscreen mode
Replace every placeholder and test the links while signed out. A route that only works for maintainers is not a usable entry point.
Turn the issue picker into the front door
GitHub’s issue template configuration can direct people to the correct destination before they create an issue.
Create .github/ISSUE_TEMPLATE/config.yml:
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://github.com/OWNER/REPOSITORY/discussions
about: Get help with setup, configuration, and usage.
- name: Send a private message
url: https://example.com/contact
about: Share account details, personal information, or private feedback.
- name: Report a security vulnerability
url: https://github.com/OWNER/REPOSITORY/security/policy
about: Follow the private security reporting instructions.
Enter fullscreen mode Exit fullscreen mode
Disabling blank issues is useful only when the provided templates cover the common paths. Otherwise, it turns structure into a barrier.
Next, add .github/ISSUE_TEMPLATE/bug.yml:
name: Bug report
description: Report a reproducible problem
title: '[Bug]: '
labels:
- 'status: needs-triage'
body:
- type: markdown
attributes:
value: |
Thanks for reporting a problem. Remove secrets and personal data before submitting.
- type: textarea
id: description
attributes:
label: What happened?
description: Describe the actual and expected behavior.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Minimal reproduction
description: Provide steps, a small repository, or a code sample.
validations:
required: true
- type: input
id: version
attributes:
label: Version
placeholder: 'v2.4.1'
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Include the operating system, runtime, browser, and relevant dependencies.
- type: checkboxes
id: checks
attributes:
label: Submission checks
options:
- label: I searched existing issues.
required: true
- label: I removed secrets and personal data.
required: true
Enter fullscreen mode Exit fullscreen mode
The form asks for evidence needed to reproduce a bug, but it does not ask reporters to diagnose the cause. Diagnosis remains the maintainer’s job.
Use a deliberately small state machine
Labels should answer what happens next, not describe every possible attribute of an issue.
Start with these states:
-
status: needs-triage— nobody has made a routing decision yet. -
status: needs-info— the reporter must provide something specific. -
status: accepted— the problem or proposal is valid, but it may not be scheduled. -
status: duplicate— another issue is the canonical thread. -
status: out-of-scope— the request does not fit the project’s boundaries.
Every open issue should have exactly one status label. Technology, component, and priority labels can be added separately, but they should not replace the workflow state.
A triage pass then becomes a repeatable checklist:
- Open the
status: needs-triagequeue. - Remove secrets or personal information immediately if any were posted.
- Check for duplicates.
- Confirm that the report belongs in this repository.
- Attempt reproduction when appropriate.
- Apply one status label.
- Write the next action in a comment.
- Assign an owner only when somebody has actually accepted responsibility.
For example, a useful needs-info response is specific:
Thanks for the report. We need the smallest configuration that reproduces this and the exact runtime version. Please add those details within 14 days; otherwise, we will close the issue and reopen it when the information is available.
Enter fullscreen mode Exit fullscreen mode
That is more actionable than simply writing “need more details.”
Choose a cadence instead of pretending to offer instant support
A support workflow needs a named owner and a review schedule. It does not necessarily need continuous monitoring.
For a small project, two scheduled triage passes per week may be enough. Put the current maintainer or rotation in a private team document, and publish only the review cadence in SUPPORT.md.
During each pass, inspect at least:
is:issue is:open label:"status: needs-triage"
is:issue is:open label:"status: needs-info" updated:<2026-07-10
is:issue is:open no:assignee label:"status: accepted"
Enter fullscreen mode Exit fullscreen mode
Replace the hard-coded date in the second query with the cutoff for your response window. Saved GitHub searches or project views can make these queues easier to revisit.
Measure the health of the workflow rather than the volume of messages:
- Age of the oldest untriaged item
- Number of items without a workflow state
- Number of accepted issues without an owner
- Private messages waiting for a routing decision
These are operational signals, not performance targets. They tell you where the process has stopped moving.
Keep the private channel from becoming a second backlog
A private channel should be an intake route, not the permanent system of record for ordinary product work.
Apply this rule to every private message:
- Reply privately if it contains account details, personal data, or another sensitive subject.
- Redirect to an existing public thread if the answer is already documented.
- Promote it to a public issue if it identifies generally useful work and the sender agrees.
- Close it explicitly if no action will be taken.
When promoting feedback, rewrite it rather than copying the original message. Remove identifying details and describe the observable problem:
## Problem
Users configuring the client without a default region receive an unclear error.
## Expected outcome
The configuration validator should identify the missing field and point to the relevant documentation.
## Source
Private feedback, shared publicly with permission and anonymized.
Enter fullscreen mode Exit fullscreen mode
The public issue becomes the canonical thread. Future updates belong there rather than being split between an inbox and GitHub.
Common failure modes
Every route says “contact us”
This sends bugs, questions, security reports, and private details into the same queue. Show users a routing choice before they compose a message.
Automation creates the appearance of support
An instant bot response does not mean a human has reviewed the report. If you automate acknowledgements, state the actual review cadence and avoid implying that work has been scheduled.
Labels accumulate without changing decisions
If an issue has 12 descriptive labels but no next action, the taxonomy is not helping. Require one workflow state and keep optional labels limited.
Private feedback never returns to the project
Useful reports can disappear inside direct messages. Add “promote, redirect, reply, or close” to every private-channel triage pass.
The maintainer silently becomes unavailable
A shared inbox still fails if everyone assumes someone else is watching it. Assign a current owner or rotation, even if the team has only two people.
Security reports use the general contact route
Keep vulnerability instructions in SECURITY.md and enable GitHub’s private vulnerability reporting when it fits the project. Do not ask reporters to send exploit details through an ordinary public form.
A hosted contact-page implementation
If you do not want to build and operate the private intake page, Knocket is one implementation example: create a shareable contact page, place its URL in the README and contact_links configuration above, and keep GitHub as the canonical location for public work. Visitors do not need an account to start a conversation; messages can also be routed to Telegram, where a quoted reply can be delivered back to the visitor.
The workflow rules remain the same regardless of the contact-page provider: appoint an owner, review the queue on a schedule, protect sensitive information, and promote suitable feedback into public issues with permission.
A maintenance checklist
Once a month, verify the system itself:
- Open every README and issue-picker link in a private browser window.
- Confirm that
SUPPORT.mddescribes the current review cadence. - Check that the security route is distinct from general contact.
- Review untriaged and unassigned queues.
- Remove obsolete labels and templates.
- Sample private conversations to confirm each received a routing decision.
- Update the named internal owner or rotation.
A good feedback channel is not defined by how many ways people can reach you. It is defined by whether every message reaches a decision.
Discussion
How does your project separate public issues from private feedback? Do you use a maintainer rotation, a scheduled triage session, or another mechanism to prevent reports from becoming an abandoned inbox?
Disclosure: I work on Knocket, so treat it as one implementation example rather than a neutral recommendation.
답글 남기기