Go Good First Issues

Leonid Bugaev
|
(Updated August 31, 2026)
|
6 min read
go beginners good-first-issue contributing

Go modules make first PRs easy to get wrong. Format the file you opened and you ship a 400-line whitespace diff. Patch internal/ and you change something forty packages already import. The mergeable first change is a TestFoo table next to the function, a flag under cmd/, or godoc on an exported type.

Generic hunting: how to find good first issues. Multi-language picking: best open source projects for beginners. This URL is the Go filter.

Open the Go hub

Go hub

Labeled good-first and help-wanted issues on active GitHub repos whose language is Go. On 2026-08-31: about 2,350 labeled issues at 10+ stars. Snapshot. It moves.

Not writing Go? The homepage is the language switch.

Read the issue body. Skip stale, claimed, and repos that have not merged lately. Then the checks in how to pick an issue that will get merged.

No roster of famous modules. The feed is the hub, ticket by ticket.

First tests are TestFoo, not a new framework

testing ships with Go. First PR: TestFoo in foo_test.go beside foo.go.

Table rows: input, want, maybe err. t.Run names the case. go test in that package is the proof.

Out of scope:

Same-package _test.go can see unexported names. An external test package cannot. Do not flip that boundary to look cleaner. Keep the table next to the function the ticket named.

CONTRIBUTING will name go test ./... or a package path. Run it on the default branch first. If the package does not build on your machine, pick another ticket. Build-tag archaeology is not issue one.

ExampleFoo in the same file is a first PR when the ticket wants a godoc snippet go test executes. That is still the testing package. It is not a docs site.

cmd/ vs internal/

Layout is the size.

cmd/ holds mains. A flag, a usage string, an exit code, a help line that lies. Isolated. Reviewers of that binary do not re-litigate the module.

Exported surface: package comment, godoc on a public func, ExampleFoo the issue already drafted. Callers compile against that. You are filling a hole they can see.

internal/ is for code the module owns. Other packages in the same module import it freely. A one-line change there is a contract change. You will be asked for tests you cannot run, or the PR will sit. A good first issue label on internal/foo is still internal/.

Start in cmd/ or on exported docs and tests. Move into internal/ after you have a merge in that repo.

gofmt / goimports: the hunk, not the package

gofmt is required. A gofmt PR is not a contribution.

Do not format the package. Do not format the 2,000-line file because the editor offered. That hunk fights every open PR on the same file.

goimports rewrites the whole import block. Add one test import and it reorders thirty lines. Revert the churn you did not need. Keep the one new import.

Rules:

A formatted test is hygiene. A package-wide format is a closed PR.

Not the compiler, not the runtime, not x/sys

gc, the runtime, and golang.org/x/sys look labeled and are still issue two. Syscall wrappers, scheduler code, assembly, cgo, unsafe that the tree trusts — those reviews are architecture. The badge means they want help. It does not mean the patch is first-timer sized.

Issue one is a leaf. After a merge in that repo, you can go inward.

Leave go.mod alone

Bump the Go version, replace, or a tool module only if the issue named it. go mod tidy that rewrites the require block because you ran it out of habit is a second review. Skip it.

One package, one ticket

  1. Filter on the Go hub. Drop stale and claimed. Confirm recent merges.
  2. Read CONTRIBUTING.md for go test and fmt. Get default branch green.
  3. Comment on one issue.
  4. Diff stays on the ticket. Hunk-only gofmt. No internal/ unless the ticket is a leaf you can lock with TestFoo. Fixes #123. Say which go test you ran. Stay for review.

Git: first pull request guide. Week-1: how to contribute to open source.

FAQ

Should I gofmt the whole package while I am here?

No. Format the hunk you touched. A whole-package gofmt or a goimports reshuffle of a 30-import block is a style PR. Reviewers close it or make you revert. If CI formats the module, let CI.

Is a TestFoo table a real first Go PR?

Yes. A table-driven TestFoo in foo_test.go next to the function the issue named is a real merge. Pulling in testify, Ginkgo, or a new harness is not. Do the ticket.

Can my first issue live in internal/?

No. internal/ is imported across the module. First PRs belong in cmd/ (a flag, a usage line) or on an exported API (godoc, ExampleFoo). After a merge in that repo, issue two can move inward.

Can my first issue be in the compiler, the runtime, or x/sys?

No. Leave gc, the runtime, and golang.org/x/sys until issue two. Those files have implicit contracts. Issue one is a leaf: a table test, a cmd/ change, exported docs.

Where do I find live Go good first issues?

On the Go hub. Filter, skip stale and claimed threads. Read CONTRIBUTING for go test and fmt. Other languages: the homepage.

Start here

Open the Go hub. Pick one unclaimed ticket that is a TestFoo table, a cmd/ flag, or exported godoc. Stay out of internal/, skip compiler/runtime/x/sys, gofmt only what you touched, keep the diff on that job, and stay for review. Not writing Go tonight? The homepage is the language switch.

Back to all articles Find projects that need your help