15 - Your First Contribution¶
What this session is¶
The whole thing. Maybe two sessions. We're going to walk through the workflow of making a real contribution to a real open-source project, end to end: fork, branch, change, test, push, PR, review, merge. This is the page the whole path has been building toward.
By the end you'll have submitted a pull request. By the time it merges (which may be days or weeks later), you'll be an open-source contributor - a small, real one. Welcome.
The whole workflow at a glance¶
The standard GitHub contribution flow, in eight steps:
- Fork the project on GitHub (a personal copy).
- Clone your fork to your machine.
- Add upstream as a remote (so you can pull future updates).
- Branch off
main. - Change the code; add a test.
- Run tests locally; make sure CI's command passes.
- Push to your fork.
- Open a PR against the upstream project.
Each step is short. The whole sequence might take 20 minutes the first time you've practiced it; 5 minutes once it's automatic.
Step 1: Fork¶
On the project's GitHub page, click Fork (top right). GitHub creates a copy at github.com/<you>/<project>. This is your personal copy; you can push anything to it without affecting the original (called upstream).
Step 2: Clone¶
In your terminal:
You now have a local copy of your fork.
Step 3: Add upstream as a remote¶
You want to be able to pull updates from the original project, not just your fork. Add it as a remote called upstream:
Check it worked:
You should see two remotes: origin (your fork) and upstream (the original).
Whenever you want to update your local copy with the latest from upstream:
(There are slicker workflows, but this is the most explicit and hardest to mess up.)
Step 4: Branch¶
Never commit directly to main. Always create a branch for your change. Common naming:
The name should hint at what the change does. Some projects have conventions in CONTRIBUTING.md; follow them.
Step 5: Make the change¶
Edit the file(s) involved in your issue. The change should be:
- Small. Touch as few files and lines as possible. A 5-line diff is easier to review than a 500-line one.
- Focused. One issue per PR. Don't bundle unrelated fixes; submit them as separate PRs.
- Tested. If the project has tests and your change has any logic, add a test. Even one is enough for a first contribution.
For code changes, follow the project's existing style. If function names are CamelCase in this project, don't introduce snake_case. If error messages start lowercase (Go convention), match it. The reviewer's job is to evaluate your change, not teach you the style.
Run gofmt -w . (or goimports -w .) on any file you've edited. This auto-formats to Go's standard layout. Most editors do it on save; do it manually if not.
Step 6: Run tests locally¶
Run exactly what CI runs. You looked at the CI workflow in page 14; replay those commands here:
go test ./... # all tests
go test -race ./... # with race detector, if the project uses goroutines
golangci-lint run # if the project uses it
Every command should be green. If a test fails, fix it before pushing. If lint complains, fix it before pushing. Pushing red CI is rude - it makes reviewers babysit your PR through cycles of "now please fix this, now please fix that."
Don't trust "it works on my machine." Run the exact commands the maintainers will run.
Step 7: Commit and push¶
Stage and commit your change:
The commit message: - First line, short. ~50 chars. Imperative mood ("Add", "Fix", not "Added" / "Fixed"). - Optional body. Blank line, then a longer description. - Reference the issue. "#123" links the PR to the issue automatically on GitHub.
If CONTRIBUTING.md mandates a format (Conventional Commits: feat:, fix:, chore:...), follow it.
If CONTRIBUTING.md requires DCO (Developer Certificate of Origin):
The -s adds Signed-off-by: Your Name <your@email> to the commit, certifying you wrote the change (or have rights to contribute it).
Push to your fork:
GitHub will print a URL - clicking it opens a pre-filled "open a pull request" page.
Step 8: Open the PR¶
On the upstream project's GitHub page, you should now see a banner suggesting "Compare & pull request." Click it.
Fill out the PR:
- Title. Mirror the commit message. Or, if you used the issue's title, match that.
- Description. What does this change? Why? What did you test? Reference the issue: "Closes #123" or "Fixes #123" - GitHub will auto-close the issue when this PR merges.
- Checklist. If the PR template has one, address every item.
Submit. CI will start. Wait for it to go green; if it goes red, look at the failing step in the GitHub UI and fix locally, then push more commits to your branch (they automatically attach to the PR).
What happens next: review¶
A maintainer will look at your PR. The possible outcomes:
- "LGTM, merging." Best case. Done.
- "Could you make these changes?" Most common. They'll leave inline comments. Address each one - either by changing the code or replying with a reason not to. Then push more commits.
- "Thanks, but we don't want this." Rare for
good first issuework, but possible if the issue was stale or you misread the requirement. Don't take it personally. Ask if there's a related issue you could pick up instead. - Silence. Sometimes happens. After a week of nothing, leave a polite comment: "Friendly bump - anything I should address?"
Code review is not personal. Even senior engineers get review comments. The skill is address the feedback efficiently without arguing about style preferences. Disagree only on substance, and only with reason.
After the merge¶
When your PR is merged:
- Update your fork's
main. Pull from upstream, push to your fork (the workflow from step 3). - Delete the branch. Locally (
git branch -d fix/issue-123-...) and on your fork (git push origin --delete fix/issue-123-...). - Take a screenshot. Yes, really. You'll be glad to have it.
- Don't immediately pick the next issue. Sit with the experience for a day. Re-read the merged code, the review comments, the discussion. The learning is in the loop, not the artifact.
A copy-paste sequence (template)¶
Here's the full sequence, copy-paste-able, for a small docs fix on a project called example-org/example-repo for issue #42:
# 1-2. Fork on GitHub, then clone:
git clone https://github.com/<you>/example-repo
cd example-repo
# 3. Add upstream:
git remote add upstream https://github.com/example-org/example-repo
git fetch upstream
# 4. Branch:
git checkout -b docs/fix-typo-in-readme
# 5. Make the change. Edit README.md.
# 6. Run tests (the project's CI commands):
go test ./...
golangci-lint run
# 7. Commit and push:
git add README.md
git commit -m "docs: fix typo in installation section (#42)"
git push origin docs/fix-typo-in-readme
# 8. Open the PR on github.com. Wait. Respond to review.
That's it. The whole thing.
After your first contribution: what next¶
Once you've landed one PR:
- Pick another issue in the same project. Familiarity compounds; your second PR will be much faster.
- After 3-5 PRs in one project, consider becoming a regular. Watch the issue tracker. Respond to issues you can answer. Review other people's PRs (you don't need to be a maintainer to leave helpful comments).
- Branch out. Use what you learned to evaluate and contribute to a Tier 2 or Tier 3 project from page 13.
- Build something of your own. Use Go to scratch a personal itch. Publish it. Iterate based on real use.
- Read this curriculum's "Go Mastery" path when you want to go from "I can contribute" to "I can architect and review."
What you might wonder¶
"What if my PR sits unreviewed for weeks?" Leave a polite check-in comment after ~1 week of silence. After 3 weeks of silence, ask in any community channel the project has (Discord, Slack, mailing list) whether you should redirect the work elsewhere. Some projects are slow; some are abandoned.
"What if a maintainer is rude?" Disengage, contribute elsewhere. There are thousands of projects. You don't owe any single project your time.
"What if I disagree with a code-review comment?" Two questions to ask yourself: (1) Is the comment about correctness, or style? Style: just do what they ask. Correctness: explain your reasoning, with a specific example. (2) Is the maintainer obviously more experienced with this codebase than you? Yes: they're probably right and you should learn from it. No: it's reasonable to push back. Either way, stay polite, stay specific.
"What if I can't make the tests pass locally?" Read the CONTRIBUTING.md for any setup steps you missed. Check the CI workflow for env variables or build flags. If still stuck, ask in the issue or PR - but be specific about what you tried.
"What if I introduced a bug in my fix?" Comes up. Push another commit fixing it. Don't squash or rewrite history unless asked - many projects squash on merge automatically.
"Can I list this on my CV?" Yes. "Open-source contributor, projects: X, Y, Z" is a real signal. Link to your specific merged PRs, not just the repos.
Done¶
You can now: - Walk through the full GitHub contribution workflow: fork → clone → branch → change → push → PR. - Run a project's CI commands locally before pushing. - Write a contribution-ready commit message. - Read and address code-review feedback. - Recover gracefully when a PR sits or gets pushback.
Done with the path¶
You started this path being told that programming was something you could learn from scratch. You've now:
- Installed Go and written your first program.
- Learned every fundamental Go concept: variables, types, control flow, functions, structs, methods, slices, maps, errors, pointers, concurrency, tests, packages, modules.
- Read a real Go open-source project and made sense of its layout.
- Picked a project, prepared a change, submitted a pull request.
What you should not do next: feel like you "know Go" now. You know what you've been taught. There is much more - networking, advanced concurrency, the JIT, the runtime, performance work, large-system design. Each is a path of its own.
What you should do: keep contributing. The way you become an engineer is by doing real work on real codebases over time. There is no shortcut.
Two recommended next paths if you want to keep going on this site:
- Go Mastery - the 24-week deep dive into the Go runtime, scheduler, GC, distributed systems. Assumes you're past where this path leaves you.
- Linux Kernel - Go programs run on a Linux kernel; understanding how it works underneath makes you a much better Go engineer.
Or just go build something. Programming pays you back when you build, not when you read.
Congratulations. You are no longer a beginner.