Skip to content

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 Python 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. When 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

Eight steps:

  1. Fork the project on GitHub.
  2. Clone your fork.
  3. Add upstream as a remote.
  4. Branch off main.
  5. Set up the dev environment, including pre-commit if used.
  6. Change the code; add a test.
  7. Run tests + linters locally.
  8. Push to your fork; open the PR.

Each step is short. The whole sequence takes 30 minutes the first time; 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.

Step 2: Clone

git clone https://github.com/<you>/<project>
cd <project>

Step 3: Add upstream as a remote

git remote add upstream https://github.com/<owner>/<project>
git fetch upstream
git remote -v

You should see origin (your fork) and upstream (the original).

To pull updates from upstream later:

git fetch upstream
git checkout main
git merge upstream/main
git push origin main

Step 4: Branch

Never commit directly to main. Always branch.

git checkout -b fix/issue-123-clarify-error-message

The name should hint at the change. Some projects have conventions; follow them.

Step 5: Set up the dev environment

Create a venv and install dev dependencies:

python -m venv .venv
source .venv/bin/activate
pip install -e .[dev]            # most projects
# or: pip install -e .[tests,docs]   # depends on what extras the project defines

If the project uses pre-commit, install the git hooks:

pre-commit install

Now every git commit will auto-run formatters/linters and fail if there's an issue (which is what you want - better to know now than after pushing).

Run the test suite to make sure everything works on your machine:

pytest

All green? Good. If not, stop. Figure out why before changing anything. Common causes: missing system dependencies, wrong Python version, the project's main is currently broken.

Step 6: Make the change

Edit the files involved. The change should be:

  • Small. Touch as few files and lines as possible. A 5-line diff is easier to review than 500.
  • Focused. One issue per PR. Don't bundle unrelated fixes.
  • Tested. If your change has logic, add a test. Even one is enough.

For code changes, follow the project's style. The pre-commit hooks usually handle formatting automatically.

Step 7: Run tests + linters locally

Replay exactly what CI runs. You looked at the CI workflow in page 14; run the same commands:

pytest                    # all tests
ruff check .              # lint
mypy src/                 # type-check (or whatever path the project uses)

Every command should be green. If something fails, 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."

If the project has multiple Python versions in CI (3.10, 3.11, 3.12, 3.13), you don't have to run them all locally. Tools like tox or nox automate this:

nox -s tests              # run tests on all configured Python versions

But for a small change, "passes on my local Python" is enough; CI will catch anything version-specific.

Step 8: Commit and push

Stage and commit:

git add <files>
git commit -m "fix: clarify error message in core.py (#123)"

If pre-commit is installed, it runs now. If it modifies files (formatting), the commit aborts; re-stage and re-commit.

Commit message conventions: - First line, short. ~50 chars. Imperative mood ("Add", not "Added"). - Optional body. Blank line, then a longer description. - Reference the issue. "#123" auto-links to it.

If CONTRIBUTING.md mandates Conventional Commits (feat:, fix:, chore:), follow it.

If DCO is required:

git commit -s -m "fix: ..."

Adds Signed-off-by: Your Name <your@email> to the commit.

Push to your fork:

git push origin fix/issue-123-clarify-error-message

GitHub prints a URL - click it to open a pre-filled PR page.

Step 9: Open the PR

On the upstream project's GitHub, you'll see a banner suggesting "Compare & pull request." Click it.

Fill out:

  • Title. Mirror the commit message. Or match the issue title.
  • Description. What does this change? Why? What did you test? Reference the issue: "Closes #123" or "Fixes #123" - GitHub auto-closes the issue when the PR merges.
  • Checklist. Address every item in the PR template.

Submit. CI starts. Wait for green; if red, look at the failing step and fix locally, then push more commits (they automatically attach to the PR).

If the project requires a CHANGELOG entry, add a line under "Unreleased" describing your change.

What happens next: review

A maintainer will look. Possible outcomes:

  1. "LGTM, merging." Best case.
  2. "Could you make these changes?" Most common. They leave inline comments. Address each - either by changing code or replying with a reason. Push more commits.
  3. "Thanks, but we don't want this." Rare for good first issue work. Don't take it personally. Ask if there's a related issue.
  4. Silence. Sometimes. After a week, 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 feedback efficiently without arguing about style preferences. Disagree only on substance.

After the merge

When your PR merges:

  • Update your fork's main (the workflow from step 3).
  • Delete the branch. Locally (git branch -d ...) and on your fork (git push origin --delete ...).
  • Take a screenshot. Really. You'll be glad later.
  • Sit with it for a day. Re-read the merged code, the review comments. The learning is in the loop.

A worked example session (template)

Full sequence for a small docs fix on example-org/example-repo 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. Set up dev env:
python -m venv .venv && source .venv/bin/activate
pip install -e .[dev]
pre-commit install
pytest    # baseline green

# 6. Make the change. Edit README.md.

# 7. Run linters locally:
ruff check .
pytest

# 8. 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

# 9. Open the PR on github.com. Wait. Respond to review.

That's the whole thing.

After your first contribution: what next

Once you've landed one PR:

  1. Pick another issue in the same project. Familiarity compounds; your second PR will be much faster.
  2. After 3-5 PRs, consider becoming a regular. Watch the issue tracker. Answer issues you can. Review other people's PRs (you don't need to be a maintainer to leave helpful comments).
  3. Branch out. Use what you learned for a Tier 2 or 3 project from page 13.
  4. Build something of your own. Use Python to scratch a personal itch. Publish it (pip install -e . then python -m build && twine upload). Iterate based on real use.
  5. Read the "AI Expert Roadmap" path on this site when you want to grow into Python's biggest current niche.

What you might wonder

"What if my PR sits unreviewed for weeks?" Polite check-in after ~1 week. After 3 weeks of silence, ask in any community channel whether to redirect. Some projects are slow; some are abandoned.

"What if a maintainer is rude?" Disengage. There are thousands of projects.

"What if I disagree with a review comment?" Two questions: (1) Is it about correctness or style? Style: do what they ask. Correctness: explain your reasoning with a specific example. (2) Are they more experienced with this codebase than you? Yes: probably right. No: reasonable to push back. Either way: stay polite, stay specific.

"What if I can't make the tests pass locally?" Re-read CONTRIBUTING.md for missed setup. Check the CI workflow for env vars. Stuck after an hour: ask in the issue or PR, with specifics about what you tried.

"What if I introduce a bug in my fix?" Comes up. Push another commit fixing it. Don't squash or rewrite history unless asked.

"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.

Done

You can now: - Walk through the full GitHub contribution workflow. - Run a project's CI commands locally before pushing. - Write a contribution-ready commit message. - Read and address code-review feedback. - Recover 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 Python and written your first program.
  • Learned every fundamental concept: variables, types, control flow, functions, classes, collections, errors, iterators, files, tests, packages.
  • Read a real Python OSS 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 Python" now. You know what you've been taught. There is much more - web frameworks, async programming, scientific computing, ML, packaging at scale. 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:

  • Python Mastery - the 24-week deep dive into CPython internals, performance, concurrency, AI runtimes. Assumes you're past where this path leaves you.
  • AI Expert Roadmap - Python is the dominant language for AI/ML. This 12-month companion takes you from math foundations through transformers, RAG, evals, and fine-tuning.

Or just go build something. Programming pays you back when you build, not when you read.

Congratulations. You are no longer a beginner.

Comments