How to Write a Pull Request Description Maintainers Will Read

Leonid Bugaev
|
(Updated August 31, 2026)
|
6 min read
pull-request beginners contributing github

The branch is already on GitHub. This page is the description: title, body, and the issue link. That is what a maintainer reads before they open the diff.

It is not fork, clone, branch, or commit. That is the first pull request guide. It is not draft vs ready, Codespaces, or the Contribute sidebar. That is how to contribute to open source on GitHub. After it merges, after your first open source contribution is stay or leave. This page stops at the text in the PR box.

Use their template, if they have one

Look for PULL_REQUEST_TEMPLATE.md on the default branch:

GitHub pre-fills that file when you open the pull request. Fill those headings. Do not delete them. Do not paste a second template you copied from a blog or a model.

If a section does not apply (Screenshot, Breaking change), write n/a and one clause why. Empty headings look like you skipped the form.

If there is no template, do not invent one. Write the four things below in short paragraphs.

Branch name, test command, DCO/CLA — those notes come from contribution guidelines and the code of conduct. The description is not a second CONTRIBUTING.md.

Title: name the issue, not “fix”

One line. Name the problem or the behavior, not a verb dump.

Bad: fix, update, typo, misc, first contribution.

Better:

The title is the inbox line. Maintainers skim dozens. fix tells them nothing. Repeat the issue title if it already names the job. Do not stamp first-pr or your username on it.

Keep it under about 70 characters if you can. Do not put Closes #123 in the title. That belongs in the body.

Body: what changed, why, how you tested

Four blocks. Short.

What changed. The behavior in the diff. One or two sentences. Files are in the Files tab; do not paste a 40-file list.

Why. The issue constraint. Panic on empty config, missing install step, the test the issue asked for. Not your internship. Not “I wanted to contribute.”

How you tested. The command you ran, or the manual check. Copy the command from CONTRIBUTING.md. If you could not run the suite, say so and say what you did instead. Do not claim CI will catch it.

Screenshots only if the change is UI: a page, a component, a rendered docs site. A parser, a test, or a CLI flag does not need a PNG. If the template has a Screenshot heading and you touched no UI, write n/a — no UI.

Put a keyword and the number in the body:

Closes #123

or Fixes #123. GitHub links the pull request to the issue and, by default, closes the issue when the PR merges into the default branch.

Use the issue you actually fixed. Do not write Closes #1 because it looks official. If the project asks for Refs #123 and does not want auto-close, use that — their guide wins. Some repos also turn auto-close off in settings; the keyword still links. Linking is the point for a first PR.

A bare #123 in a sentence is a mention. Closes / Fixes is the keyword.

What not to paste

Your resume. School, stack, “open to work.” The PR is a patch, not a job thread.

An AI dump. A generated essay, a fake test plan you did not run, a bullet list of “improvements” the issue did not ask for. If you used a model to draft sentences, cut it to what you can defend in review.

A 40-file drive-by list. src/a.py, src/b.py, and so on. Maintainers have the Files tab. A description that is only paths usually means the diff is unscoped.

Also skip: “Please merge”, “Hope this helps”, emoji walls, and a second copy of the issue body.

Good vs bad

Same first-timer, same empty-config crash. Generic repo. No famous names.

Bad

Title: fix

Hi I am a student looking for internships.

I used AI to fix the bug and also cleaned up nearby files.

Please merge!!

Good

Title: Return a clear error when config.yaml is empty

Closes #123

What changed: parseConfig returns an error on an empty config file instead of panicking on a nil map.

Why: `tool run` crashes when config.yaml exists but has no keys. The issue asked for a clear error, not a default config.

How I tested: added a table case in parse_config_test.go; ran `go test ./internal/config`. No UI.

n/a on screenshots.

The good one is boring on purpose. A maintainer can review it without guessing.

FAQ

They have PULL_REQUEST_TEMPLATE.md. Do I write my own?

No. Fill theirs. Do not add a second outline. If a heading does not apply, write n/a plus why.

Is “fix” or “update” a good title?

No. Name the issue: the error, the missing docs, the test. fix is a status, not a title.

Does Closes #123 go in the title?

No. Put Closes #123 or Fixes #123 in the body so GitHub links the issue (and can close it on merge). Follow CONTRIBUTING if they want Refs instead.

Do I need screenshots?

Only if you changed UI. A test, a parser, or a CLI flag needs the command you ran, not a PNG. If the template asks and you have no UI, write n/a.

Can I paste my resume, a model dump, or every file I touched?

No. The description is what changed, why, how you tested, and the issue link. Resume goes on a job site. Generated essays get closed. The Files tab already has the paths.

Back to all articles Find projects that need your help