You've found a bug in a library you use every day. You know how to fix it. You even have the code written. But between your local editor and that fix landing in the official repository lies a structured workflow that most beginner guides skim over — and that gap is exactly where first-time contributors stall. This article walks through the entire contribution lifecycle for open source projects on GitHub, step by step, so you understand not just what to do but why each stage exists.
Why Open Source Projects Use a Formal Contribution Workflow
A popular open source project might receive dozens or hundreds of proposed changes every week, from contributors who have never spoken to each other and whose code may conflict, break tests, or introduce security issues. The fork-branch-pull request model exists to let maintainers accept good work from strangers safely, without giving everyone direct write access to the main codebase.
Understanding this goal reframes every step. Each stage of the workflow isn't bureaucracy — it's a quality gate that protects users who depend on the project.

As an Amazon Associate, I earn from qualifying purchases.
Step 1: Fork the Repository
When you fork a repository on GitHub, you create a complete copy of it under your own GitHub account. This is your personal sandbox. You can do anything to it — break it, rewrite it, delete files — without affecting the original project at all.
Forking is distinct from cloning. Cloning downloads a repository to your local machine. Forking creates a new remote repository on GitHub that you own. In practice, you do both: you fork on GitHub, then clone your fork to your machine to work on it locally.
git clone https://github.com/YOUR-USERNAME/PROJECT-NAME.git
After cloning, it's good practice to add the original repository as a second remote, conventionally named upstream. This lets you pull in changes the maintainers make while you're working on your fix.
git remote add upstream https://github.com/ORIGINAL-OWNER/PROJECT-NAME.git
Step 2: Create a Branch
Never work directly on the main (or master) branch of your fork. Instead, create a new branch specifically for the change you're making. This is a core discipline of collaborative development, not an optional nicety.
As an Amazon Associate, we earn from qualifying purchases.
git checkout -b fix/button-focus-trap
A well-named branch tells maintainers at a glance what the change is about before they read a single line of code. Common conventions prefix the branch name with the type of change: fix/ for bug fixes, feat/ for new features, docs/ for documentation updates, chore/ for maintenance tasks.
Working on a dedicated branch also means you can keep your fork's main branch clean and in sync with upstream. If the maintainers ask you to make changes before merging, you simply update the same branch — your main stays untouched.
Step 3: Read CONTRIBUTING.md Before You Write a Line
Most established projects include a CONTRIBUTING.md file in the root of the repository. Read it before you start coding. It will tell you:
- How to set up the development environment
- Which coding style or linter the project enforces
- Whether you need to sign a Contributor License Agreement (CLA)
- How to write commit messages the project expects
- Whether an issue or discussion should be opened before submitting a PR
Ignoring this file is the single most common reason first contributions get rejected immediately, before a maintainer even reads the code. Maintainers are usually volunteers managing limited time; a PR that ignores the stated conventions creates extra work for them.
Step 4: Make Your Changes Locally
Now you write code. A few principles apply universally across well-run projects:
Keep the change focused
One pull request should do one thing. If you're fixing a bug, fix the bug — don't also refactor unrelated functions or update the README while you're there. Mixing concerns makes code review harder and increases the chance your PR is rejected or delayed. Maintainers may love part of your change and be uncertain about another part; a focused PR forces a cleaner decision.
Write or update tests
If the project has a test suite (most serious ones do), your change should come with tests. For a bug fix, write a test that fails before your fix and passes after it. This proves the bug existed, proves your fix resolves it, and prevents the same bug from silently returning later.
Run the test suite locally before committing
It takes a few minutes locally. It saves everyone time. A PR that immediately fails automated tests signals that the contributor didn't run the checks themselves.
Step 5: Commit with a Clear Message
A commit message is documentation. Future contributors — including you, six months from now — will read it to understand why the code changed, not just what changed (the diff shows that).
Many projects follow the Conventional Commits specification or a similar format:
fix: correct focus trap in modal dialog
When a modal was opened programmatically, the focus trap did not
initialize correctly, allowing keyboard users to tab outside the
modal. This fix ensures focusTrap.activate() is called after the
DOM node is fully rendered.
Closes #412
The subject line is short and imperative. The body explains the problem and the reasoning. The footer references the issue. This pattern is easy to skim in a git log and invaluable when debugging regressions.
Step 6: Push to Your Fork and Open a Pull Request
Once you're satisfied with the change, push your branch to your fork on GitHub:
git push origin fix/button-focus-trap
GitHub will usually show a prompt to open a pull request immediately. The PR form is your first and often only chance to communicate with maintainers in writing before they review your code. Use it well.
Writing a useful PR description
A good PR description answers three questions:
- What does this change do? Summarize the change in plain language.
- Why is this change needed? Reference the issue it fixes, or explain the problem if no issue exists.
- How did you verify it works? Mention which tests you ran, or show a screenshot if the change is visual.
Many projects provide a PR template that structures this for you — fill it out completely, even if some sections feel redundant with the commit message.
Step 7: Automated Checks Run Against Your PR
As soon as your PR is opened, you'll typically see a series of status checks appear. These are configured through the project's CI/CD pipeline — usually GitHub Actions, CircleCI, or a similar system — and they run automatically without any maintainer involvement.
Common automated checks include:
- Linting: Does the code conform to the project's style rules?
- Unit and integration tests: Do all existing tests still pass? Do your new tests pass?
- Type checking: If the project uses TypeScript or a typed language, are types correct?
- Build: Does the project still compile or bundle successfully?
- Coverage thresholds: Some projects require that test coverage doesn't decrease.
If any check fails, you need to fix it before a maintainer will review your code. Click the failing check, read the log, understand the error, push a new commit to the same branch — the PR updates automatically.
Step 8: Code Review
This is where many beginners feel the most anxiety, but it's also the most valuable part of contributing. A maintainer (or a designated reviewer) will read your diff line by line and leave comments.
What reviewers are actually looking for
Beyond correctness, experienced reviewers think about edge cases your change might not handle, whether the approach fits the project's architectural direction, whether the change is backwards compatible, and whether the code will be readable and maintainable by the next person who touches it.
How to respond to review comments
Respond to every comment, even if just to acknowledge it. If a reviewer asks you to change something and you disagree, explain your reasoning calmly — maintainers are not always right, and good projects expect respectful discussion. If you agree with feedback, make the change, push a new commit, and reply to the comment with something brief like "Done in abc1234."
Don't force-push to rewrite history on a PR branch that's under active review. It makes it hard for reviewers to see what changed between review rounds. Use new commits; the maintainer may squash them on merge anyway.
Requested changes vs. approved
GitHub's review system has three outcomes: Comment (general feedback, no formal verdict), Approve (the reviewer is happy), and Request Changes (the reviewer requires specific fixes before approval). A PR typically needs at least one approval from a maintainer before it can be merged, and many projects require two.
Step 9: Keeping Your Branch Up to Date
If the project is active, its main branch may have moved forward while your PR was under review. You'll need to sync your branch to resolve any conflicts that have emerged.
git fetch upstream
git rebase upstream/main
Rebasing replays your commits on top of the latest main, which keeps the history linear. Some projects prefer git merge upstream/main instead — follow whatever the contributing guide says. If there are conflicts, Git will pause and ask you to resolve them file by file before continuing.
Step 10: Merge
Once all checks pass and the required approvals are in place, a maintainer with write access to the repository merges your PR. You have no control over this step — it's entirely theirs. Depending on the project's preferences, they might use a regular merge commit, a squash merge (collapsing all your commits into one), or a rebase merge.
After the merge, your code is in the official codebase. It will ship to users in the next release. Your GitHub profile shows the contribution. The issue is closed.
What to Do If Your PR Stalls
Maintainers are often volunteers with day jobs. A PR going quiet for a week or two is normal. After a reasonable wait — two to three weeks is a common guideline — it's entirely appropriate to leave a polite comment asking if there's any feedback or anything blocking review. Don't ping repeatedly or demand a timeline.
If a PR is closed without merging, read the reason carefully. It might be that the feature doesn't fit the project's direction, that a similar change was already merged elsewhere, or that the approach needs a fundamental rethink. This is not a personal rejection. Ask a clarifying question, learn from the feedback, and apply it to your next contribution.
The Bigger Picture
The fork-branch-PR workflow might feel heavyweight for a one-line fix. But it scales — the same process works for a typo correction and for a major new feature, and it ensures that every change, regardless of who wrote it, gets the same safety checks. Understanding this workflow doesn't just help you contribute; it shows you how professional software development manages change at scale, which is directly applicable to team work in any engineering role.
Your first merged PR is rarely perfect. That's fine. The point of the process is that imperfect first attempts get improved through review before they reach users, and contributors learn faster from honest feedback on real code than from any tutorial.


