Skip to content

CONTRIBUTING.md: 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 is 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 well-written CONTRIBUTING.md is the answer to all of those situations before they happen. It is the first thing a potential contributor reads when they want to get involved, and it sets the tone for every interaction that follows. Done right, it reduces back-and-forth, keeps the project moving, and signals to contributors that the project is organized and 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 is 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 point is straightforward: GitHub puts the file in front of contributors at exactly the moment they need it most.

The file answers three questions every contributor has:

  1. How do I report a problem?
  2. How do I submit a change?
  3. 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 are not 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 is not hostile, it is 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 not only reduces duplicates but also teaches contributors to find existing context that might already answer their question.

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 deserves emphasis. Asking contributors to discuss significant work first before writing code is one of the most valuable things a CONTRIBUTING.md can do. A contributor might spend a week building a feature that is not 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 is 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 genuinely 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) is important. GitHub's draft PR feature exists precisely for this: it signals to maintainers that the work is not ready for review, prevents accidental merges, and still allows CI to run. Requiring contributors to use it sets a clear protocol that avoids the maintainer guessing whether a PR is ready.

Contributor Flow

A visual walkthrough of the contributor workflow reduces the activation energy for first-time contributors significantly. 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. Including it in the example normalizes the practice so contributors do it automatically rather than needing 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 is 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:

  1. Mark the conversation as resolved.
  2. 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. The payoff compounds over time: fewer incomplete issues, fewer pull requests that need major rework, and a cleaner contribution history that is easier to maintain.

A few structural tips:

  • Put the file in .github/CONTRIBUTING.md rather 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.md that 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. The goal is completeness without overwhelming detail: 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. It is a visible signal to potential contributors that the project is actively maintained and organized, which matters more than it might seem when someone is deciding whether a project is worth investing time in.