Writing Practical Contribution Guidelines for GitHub Repositories
Every open-source project on GitHub eventually reaches the point where someone outside the immediate author wants to help. They open an issue that's missing half the information needed to reproduce it. They submit a pull request on the main branch that touches a dozen unrelated files. Or they send a message asking where to start.
A good CONTRIBUTING.md heads off all three. It's usually the first thing someone reads before they get involved. Done right, it cuts down the back-and-forth and tells contributors the project is organized enough to be worth their time.
This post walks through how I write CONTRIBUTING.md files for my open-source projects: what goes in them, why each section matters, and how to write guidelines that contributors will actually follow.
What Is CONTRIBUTING.md?¶
GitHub has native support for a CONTRIBUTING.md file. When it's present in a repository's root, in the docs/ directory, or in the .github/ directory, GitHub automatically surfaces a link to it whenever someone opens a new issue or pull request. The GitHub documentation on community profiles covers the mechanics, but the short version is that GitHub puts the file in front of people right when they're about to file something.
The file answers three questions every contributor has:
- How do I report a problem?
- How do I submit a change?
- What standards does this project hold contributions to?
Without a CONTRIBUTING.md, those questions get answered inconsistently, if at all.
Opening Section¶
Start with a short welcome that acknowledges the value of contributions without being effusive about it. Two or three sentences is plenty:
# Contributing Guidelines
We greatly value feedback and contributions from our community.
Please review this document before submitting any issues or pull requests to ensure we
have all the necessary information to effectively collaborate on your contribution.
Follow that with a tip pointing contributors toward documentation and discussions for questions that aren't bugs or proposed changes. This keeps issues focused on actionable items:
> [!TIP]
> If you need help or have questions, please refer to the
> [documentation](https://github.com/<org>/<repo>/tree/main/docs) or open a
> [discussion](https://github.com/<org>/<repo>/discussions).
The GitHub alert syntax (> [!TIP], > [!NOTE], > [!WARNING], > [!IMPORTANT]) renders as styled callout blocks on GitHub. Using it consistently throughout the file gives contributors visual cues about what is advisory versus what is a hard requirement.
Issues¶
The Issues section establishes what a good bug report looks like and sets expectations about what happens to reports that miss the mark.
## Issues
Use [GitHub issues](https://github.com/<org>/<repo>/issues) to report bugs or suggest
enhancements using the following guidelines.
> [!WARNING]
> Issues that do not follow the guidelines may be closed by the maintainers without
> further investigation.
Before opening an issue, please search existing issues to avoid duplicates.
When opening an issue, use the provided issue form to ensure that you provide all the
necessary details. These details are important for maintainers to understand and
reproduce the issue.
A few things worth calling out here.
The warning isn't hostile, it's honest. Incomplete issues consume maintainer time with clarification requests, often never get resolved anyway, and clutter the issue tracker for everyone. Telling contributors upfront that incomplete issues will be closed is a kindness. It sets a clear standard rather than leaving contributors wondering why their issue never received a response.
Issue forms do the heavy lifting. GitHub issue forms (ISSUE_TEMPLATE/ directory in .github/) let you define structured templates with required fields, dropdowns, and checkboxes. When contributors use them, they arrive with version numbers, reproduction steps, and relevant configuration already filled in. Enforcing their use is much more effective than asking contributors to include information in freeform text.
Search first. Duplicate issues multiply the work for maintainers and dilute the signal in the tracker. Making "search existing issues before opening a new one" an explicit requirement cuts duplicates, and contributors often find their answer already sitting in an old thread.
Add tips pointing to GitHub's documentation for formatting code and referencing issues. Contributors who are new to GitHub may not know about fenced code blocks, inline code formatting, or that typing #123 in a comment automatically links to issue 123:
> [!TIP]
> - Learn about [formatting code on GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code).
> - Learn about [referencing issues](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#referencing-issues-and-pull-requests).
> - Learn about [creating a GitHub Gist](https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists) for sharing large code samples.
Pull Requests¶
The Pull Requests section is where most of the work happens in a CONTRIBUTING.md. It covers two distinct phases: what to do before you open a pull request, and what to do when you open one. Keeping these separate makes the checklist easier to follow.
## Pull Requests
Use GitHub pull requests to propose changes to the codebase using the following guidelines.
> [!WARNING]
> Pull requests that do not follow the guidelines may be closed by the maintainers
> without further review.
Before Submitting¶
The pre-submission checklist is where you encode the project's quality bar. My version looks like this:
**Before** submitting a pull request, ensure that:
1. You have opened a discussion to discuss any **significant** work with the
maintainer(s). This ensures your contribution is aligned with the project's direction
and avoids unnecessary work.
2. You have identified or opened an issue. This keeps the contribution focused on a
specific topic and avoids duplicating effort.
3. You have forked the repository. Refer to the
[GitHub documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo)
for help.
4. You are working against the latest source on the `main` branch. You may need to
rebase your branch against the latest `main`.
5. You have created a topic branch based on `main`. Do not work directly on `main`.
6. You have modified the source based on logical units of work. Pull requests that
contain multiple unrelated changes will be rejected.
7. You have followed the existing style and conventions of the project.
8. You have added tests for your changes.
9. You have used [Conventional Commits](https://conventionalcommits.org) format for
commit messages.
10. You have signed-off and committed your changes using
[clear commit messages](https://cbea.ms/git-commit).
Item 1 matters most. Asking contributors to discuss significant work before writing code is one of the most useful things a CONTRIBUTING.md can do. A contributor might spend a week building a feature that isn't aligned with the project direction, or that was deliberately left out of scope. Catching that in a five-minute discussion thread saves everyone time. For small, obvious fixes this step can be skipped; that's what "significant" qualifies.
Item 6 (the logical units requirement) is the one most pull requests violate. A PR that reformats code, fixes a bug, and adds a new feature is harder to review, harder to revert if something breaks, and harder to understand months later from a commit log. Scoped PRs are faster to review and merge.
Items 9 and 10 set the commit message standard. I link to both Conventional Commits and How to Write a Git Commit Message as the two complementary references for this. One covers the structural format; the other covers the content.
When Opening¶
When opening a pull request, ensure that:
1. You title your pull request using the
[Conventional Commits](https://conventionalcommits.org) format.
2. You provide a detailed description of the changes in the pull request template.
3. You open any work-in-progress pull requests as a draft.
4. You mark the pull request as ready for review when you are ready for it to be reviewed.
5. You follow the status checks for the pull request to ensure that all checks are passing.
6. You stay involved in the conversation with the maintainers.
The draft pull request requirement (item 3) matters. Draft pull requests exist for exactly this: it signals to maintainers that the work isn't ready for review, prevents accidental merges, and still allows CI to run. Requiring it means the maintainer never has to guess whether a PR is ready.
Contributor Flow¶
First-time contributors get unstuck faster when they can see the whole workflow. I include a concrete shell script example showing the full flow from forking through opening the pull request:
### Contributor Flow
This is an outline of the contributor workflow:
```shell
git remote add upstream https://github.com/<org>/<repo>.git
git checkout -b feat/add-x main
git commit --signoff --message "feat: add support for x
Added support for x.
Ref: #123"
git push origin feat/add-x
```
The --signoff flag automatically appends a Signed-off-by trailer to the commit message. This is a Developer Certificate of Origin (DCO) mechanism that many projects require as a lightweight record that the contributor has the right to submit the work under the project's license. Putting it in the example means contributors pick it up by copying, and nobody needs a review comment to remind them.
The topic branch name (feat/add-x) mirrors the Conventional Commits type prefix. Keeping branch names aligned with commit message types makes it easier to scan a list of open branches and understand what each one is for.
Formatting Commit Messages¶
Spend a paragraph on commit messages specifically. Contributors who are new to structured commit formats benefit from seeing a complete example and having the standards linked explicitly:
### Formatting Commit Messages
Follow the conventions on
[How to Write a Git Commit Message](https://cbea.ms/git-commit) and use
[Conventional Commits](https://conventionalcommits.org).
Be sure to include any related GitHub issue references in the commit message.
Example:
```text
feat: add support for x
Added support for x.
Signed-off-by: Jane Doe <[email protected]>
Ref: #123
```
The key things the example demonstrates: a short imperative subject line using the Conventional Commits format, a blank line separating the subject from the body, a body that explains what and why (not how), and an issue reference. The Signed-off-by trailer is appended automatically when you pass --signoff to git commit.
Staying in Sync and Updating Pull Requests¶
Two more practical sections round out the file: keeping a branch current with upstream, and updating an existing pull request after review feedback.
### Stay In Sync With Upstream
When your branch gets out of sync with the `upstream/main` branch, use the following:
```shell
git checkout feat/add-x
git fetch --all
git rebase upstream/main
git push --force-with-lease origin feat/add-x
```
### Updating Pull Requests
If your pull request needs changes based on review, squash related changes into existing
commits rather than adding new ones.
For a single-commit PR or changes to the most recent commit:
```shell
git add .
git commit --amend
git push --force-with-lease origin feat/add-x
```
For squashing into an earlier commit:
```shell
git add .
git commit --fixup <commit>
git rebase --interactive --autosquash upstream/main
git push --force-with-lease origin feat/add-x
```
Using --force-with-lease instead of --force is worth calling out in a code review or in a footnote. It's a safer force-push that fails if the remote branch has been updated since your last fetch, protecting against accidentally overwriting commits that someone else has added to your branch.
When contributors respond to review comments, ask them to:
- Mark the conversation as resolved.
- Note the commit SHA that addresses each comment.
This two-step response gives reviewers a direct way to verify the issue was fixed without reading through the entire diff again.
Putting It Together¶
Assembling these sections into a complete file takes less than an hour. It pays off over time: fewer incomplete issues, fewer pull requests that need major rework, and a cleaner contribution history that's easier to maintain.
A few structural tips:
- Put the file in
.github/CONTRIBUTING.mdrather than the project root. GitHub recognizes it in either location, but.github/keeps the root directory clean and co-locates it with other GitHub-specific files like issue templates and workflow definitions. - Use reference-style links at the bottom of the file for the repeated URLs. It makes the body text easier to read and keeps links consistent.
- Link to documentation and discussions prominently. Most questions from new contributors are documentation questions, not bug reports. Routing them to discussions rather than issues keeps the issue tracker focused on actionable problems.
- Revisit the file when your process changes. A
CONTRIBUTING.mdthat describes a workflow you no longer follow is worse than no guidelines at all. Treat it as living documentation.
The sections above are what I include as a baseline. Projects with specific tooling or testing requirements should add steps for those to the pre-submission checklist. Aim for complete without being exhausting: a contributor should be able to read the file end-to-end in under five minutes and know exactly what to do.
GitHub's community health features also include a community profile that shows which recommended files are present in a repository. CONTRIBUTING.md is one of them. People deciding whether to spend time on your project do look at that checklist.