DevForge LogoDevForge
Learning Tracks

The 10 PR Journey

Ten pull requests, in order, from one we review ourselves to one a real project keeps. You are not aiming for ten checkmarks. You are aiming for one contribution you can talk about for fifteen minutes, and nine that made it possible.

Milestones signed off0 / 10

Roughly one milestone every two weeks — about a semester. Milestones 7, 9 and 10 will overlap, and should.

The six rules

A closed PR still counts.
Every milestone except the last completes on a good reflection, not on a merge. You do not control whether a maintainer merges you; you control what you learned.
No pull request before the issue.
From milestone 3 onward you comment first and wait for a reply. Unannounced PRs are the single fastest way to waste a maintainer's afternoon.
One repo, at least three milestones.
Depth beats breadth. Maintainers review known names properly and drive-by contributors barely at all — and you cannot do milestone 10 in a codebase you met last week.
Milestones 1 and 2 stay in-house on purpose.
You do not take training wheels into a stranger's repository. Learn the mechanics where the cost of getting it wrong is a teammate's five minutes.
Reflections are due within 48 hours of the review.
Written at the end of the semester they are fiction. Written the same week they are the thing you actually bring to an interview.
The goal is one deep story, not ten checkmarks.
No interviewer is moved by ten pull requests. They are moved by one you can explain for fifteen minutes. The other nine are what make that one possible.

The ten milestones

PR 01This workbook One evening

Sign the workbook

Open a pull request to the NST-DEVFORGE/workbook repo that adds your own signature file. Your first PR is to us, not to a stranger.

How to do it · 12 steps
  1. 1Open the workbook repo (link below) and read its README once, top to bottom.
  2. 2Click Fork. You now own a copy at github.com/<you>/workbook.
  3. 3Clone your fork, not ours: git clone https://github.com/<you>/workbook.git, then cd workbook.
  4. 4Make a branch before touching anything: git checkout -b sign/<your-github-username>.
  5. 5Copy the template to a file named after your exact GitHub username: cp signatures/_template.md signatures/<your-github-username>.md. Case matters.
  6. 6Fill it in: your name as the heading, then Batch, one honest line for I'm here to, and One thing I've built ("nothing yet" is fine). Keep the - **GitHub:** @<your-github-username> line exactly — the automated check reads it.
  7. 7Commit and push: git add signatures/<your-github-username>.md, then git commit -m 'Sign the workbook: <your name>', then git push -u origin sign/<your-github-username>.
  8. 8On GitHub, click Compare & pull request. Base: NST-DEVFORGE/workbook · main. Head: your fork · your branch. Files changed must show exactly one file. Fill in the PR template.
  9. 9Within a minute the Validate signature check runs and the workbook bot comments on your PR. If something is wrong, the comment lists every problem and how to fix it.
  10. 10Fix what the bot lists on the same branch, commit, push. The check re-runs and the comment updates — do not open a second PR.
  11. 11Once the check is green, the bot merges your PR automatically and leaves a few polish tips.
  12. 12Once it is merged, paste the PR URL (https://github.com/NST-DEVFORGE/workbook/pull/<number>) into milestone 1 below and write your reflection.
Done when
  • You worked on a branch, not on main — your PR shows one file changed, not forty
  • The Validate signature check is green
  • If the bot asked for fixes, you pushed them to the same PR (not a new one)
  • The bot merged your PR

Committing to main on your fork, then wondering why the PR contains everyone else's work. If your diff has files you did not touch, close it and start the branch again.

Also answer: Which part of fork → branch → commit → PR did you have to look up? Be honest — everyone looks something up here.

PR 02A DevForge repo Two to four evenings

Fix something that is ours

A real code change in a DevForge repo — the portal, a club project, a script. Real review, forgiving maintainer.

How to do it · 9 steps
  1. 1Browse the DevForge repos and their open issues. Look for good first issue, or something small you have hit yourself on the portal.
  2. 2If there is no issue for it, open one: what is wrong, where, and how you would fix it.
  3. 3Comment /assign on the issue. A bot assigns you if it is free: one open issue per person, and /unassign hands it back. No code before you are assigned.
  4. 4Fork the repo on GitHub, clone your fork, and get it running locally (npm install, then npm run dev). Setup is part of the milestone.
  5. 5Branch off the latest main: git checkout -b fix/<short-description>.
  6. 6Make the smallest change that fixes it — under ~50 lines. Run npm run lint, npm test and npm run build before pushing: they are exactly what CI runs.
  7. 7Open the PR from your fork into NST-DEVFORGE/DevForge · main ("compare across forks"), with Fixes #<issue number>, a one-line summary, and a screenshot if anything visible changed.
  8. 8Wait for CI (Lint, Test, Type-check & build) to go green. If a check fails, open its log and fix it yourself.
  9. 9Address the review on the same branch until it is merged, then submit the PR URL here.
Done when
  • An issue exists and is assigned to you before you write code
  • The diff is under ~50 lines
  • CI is green without anyone re-running it for you
  • Reviewed by a maintainer and merged

Picking something too big because it sounded more impressive. If your diff passes 100 lines at milestone 2, you chose wrong — split it or pick again.

Also answer: How long between understanding the bug and having a fix? Where did that time actually go — reading, setup, or typing?

PR 03A real outside project About a week of watching one repo

File a bug nobody can dismiss

A reproducible issue on a real outside project. No code in this milestone at all.

How to do it · 7 steps
  1. 1Pick one outside project you actually use, or one from our starter list, and plan to stay in it for several milestones.
  2. 2Read its README, CONTRIBUTING.md and issue templates before anything else.
  3. 3Use the project until something breaks, or reproduce an open bug on the latest release.
  4. 4Search open and closed issues for it. Note the search terms you used and the closest issue you found.
  5. 5Cut the reproduction down to the fewest steps that still show the bug.
  6. 6File the issue using their template: version, OS, commit SHA, numbered steps, expected vs. actual as separate sections, and a link to the closest existing issue.
  7. 7Watch it and reply promptly to any question. Once a maintainer responds — even with 'duplicate' — submit the issue URL here.
Done when
  • Exact version, OS, and commit SHA
  • Minimal steps a stranger can follow without asking you anything
  • Expected behaviour vs. what actually happened, stated separately
  • You searched the existing issues first and linked the closest one you found
  • A maintainer responded — any response counts, including 'duplicate'

"It doesn't work." Also: filing before searching. Being a duplicate is fine if you say what you searched for; being unreproducible is not.

Also answer: Paste the maintainer's first reply verbatim. What did it assume you already knew?

PR 04A real outside project About a week

Add a test for something that already works

Cover existing untested behaviour. No behaviour change. This is the highest-acceptance PR a beginner can open.

How to do it · 7 steps
  1. 1Stay in the same project. Clone it and get the full test suite passing locally before you change anything.
  2. 2Find behaviour with no test: run coverage if the project has it, or read a module and then look for its test file.
  3. 3Read three or four existing tests and copy their structure, naming and helpers exactly.
  4. 4Write your test and watch it pass.
  5. 5Break the source line it covers on purpose and watch the test fail. Copy that failure output for your reflection, then revert.
  6. 6Commit only the test file(s), open a PR titled something like test: cover <behaviour>, and explain what is now covered.
  7. 7Get CI green, respond to review, then submit the PR URL here.
Done when
  • You broke a line of the source on purpose and watched your test fail — output pasted in the reflection
  • It follows the repo's existing test conventions, not your own
  • You can run the full suite locally
  • CI green

Writing a test that passes even when the feature is broken. If you did not watch it fail, you did not write a test — you wrote a comment that takes 40ms to run.

Also answer: What did reading the test suite tell you about this codebase that reading the source did not?

PR 05A real outside project About two weeks

Fix the bug

Fix the issue you filed at milestone 3, or a good-first-issue that a maintainer assigned to you.

How to do it · 6 steps
  1. 1Take the issue you filed at milestone 3, or a good first issue in the same project.
  2. 2Comment on it: say what you think the cause is and how you would fix it, and ask to take it.
  3. 3Wait for a maintainer to reply. If someone else is already on it, pick a different issue.
  4. 4Write a failing test that reproduces the bug first, then write the fix that makes it pass.
  5. 5Open the PR with Fixes #<issue number>, the root cause in one or two sentences, and how you tested it.
  6. 6Respond to every review comment — change the code or explain why not — until it is merged or closed. Then submit the PR URL here.
Done when
  • You commented on the issue and got a reply before writing code
  • The fix ships with a test — the skill you built at milestone 4
  • The PR closes the issue with 'Fixes #N'
  • At least one round of review that you responded to

Opening the PR before commenting on the issue. Two people silently fixing the same bug is how contributors burn out and how maintainers stop labelling issues for beginners.

Also answer: What was the actual root cause, and what did you believe it was during the first hour?

PR 06A real outside project Three to five days

Write the documentation you needed

Document the exact thing that confused you at milestone 4 or 5. Now you have earned this PR.

How to do it · 6 steps
  1. 1Go back to your notes from milestones 4 and 5 and find the moment you were stuck the longest.
  2. 2Find where the docs should have explained it: the README, the docs site, a docstring, CONTRIBUTING.md.
  3. 3For anything larger than a paragraph, open an issue first proposing the change.
  4. 4Write it for the person you were that day: a worked example, a new section, or a rewrite of the confusing part.
  5. 5Open the PR and link the code or issue that confused you. Say who the change is for.
  6. 6Respond to the maintainer's feedback, then submit the PR URL here.
Done when
  • Not a typo fix — a new explanation, a worked example, or a section you rewrote
  • You can name the moment you were confused and link the code that confused you
  • Merged, or carrying maintainer feedback you have responded to

This is the milestone that looks easy and is not. If you cannot point at the hour you were stuck, you have not earned it yet — go back and finish 4.

Also answer: What did the existing docs assume about the reader that turned out to be untrue for you?

PR 07A real outside project Three to four weeks

Ship a feature you negotiated first

A change the maintainers agreed to before you built it. The conversation is the milestone; the code is the receipt.

How to do it · 6 steps
  1. 1Find a missing feature in the same project — ideally one users have already asked for in issues.
  2. 2Open an issue or discussion proposing it: the problem, the proposed behaviour, and what is out of scope.
  3. 3Wait for a maintainer to agree. Adjust the scope they push back on — that negotiation is the milestone.
  4. 4Build only the agreed scope. Add tests and docs as the project expects.
  5. 5Open the PR linking the conversation, and state which parts of the agreed scope it covers.
  6. 6Keep engaging on review until it is merged, or open with a maintainer actively reviewing it. Then submit the PR URL here.
Done when
  • An issue or discussion where you proposed it and a maintainer said some version of yes
  • The scope you shipped matches the scope that was agreed
  • The PR links that conversation
  • Merged, or open with active maintainer engagement

Building first and asking after. Second trap: the unsolicited refactor. If your PR title starts with 'refactor' and nobody asked for it, expect it closed. Refactor when a reviewer asks, inside the PR they asked in.

Also answer: What did the maintainer change about your proposal before agreeing to it?

PR 08A real outside project About a week

Review someone else's work

Two reviews: one on a club member's PR, one on a stranger's. Sit on the other side of the table.

How to do it · 6 steps
  1. 1Club review: pick an open PR on a DevForge repo, check out the branch locally and run it.
  2. 2Stranger review: pick an open PR in your outside project, in an area you now know.
  3. 3Read the linked issue first so you know what the PR is meant to do.
  4. 4Leave at least one specific, actionable comment on a line of code, and one real question about something you did not understand.
  5. 5Say in the review whether you ran the code or only read it.
  6. 6Submit the stranger's PR URL here (it must be outside DevForge), and put both review links in your reflection.
Done when
  • Each review carries at least one specific, actionable comment — 'LGTM' is not a review
  • You ran or genuinely read the code, and can say which
  • You asked at least one real question about something you did not understand

Rubber-stamping, and its mirror image — nitpicking whitespace and naming that a linter already handles. Both tell the author you did not read it.

Also answer: What did you fail to understand in their code, and did you say so out loud in the review?

PR 09A real outside project However long it takes

Survive a hard review

A PR that took three or more rounds, or one that got closed. This is the only milestone you cannot complete by getting it right first time.

How to do it · 5 steps
  1. 1This one is not planned — it happens to one of your PRs. Any PR from milestone 5 onward can count.
  2. 2When a review pushes back, answer every comment: change the code, or explain your reasoning once.
  3. 3If the answer is still no after the second round, accept it. Thank them, and close or narrow the PR.
  4. 4Never go quiet for more than a few days in an active review. If you need time, say so.
  5. 5Once it reaches three or more rounds, or gets closed, submit the PR URL and write the reflection honestly.
Done when
  • Three or more rounds of review that you worked through — or a closed PR
  • A closed PR completes this milestone in full. That is the point of it
  • You did not argue past the second 'no', and you did not disappear

Arguing, or ghosting. They end the same way, and maintainers remember both.

Also answer: Quote the harshest piece of feedback you received. Was it right? What would you tell yourself the day before you opened that PR?

PR 10A real outside project The rest of the semester

The one you would defend

A contribution the project actually keeps, and that you can talk about for fifteen minutes without notes.

How to do it · 6 steps
  1. 1Choose the contribution you understand best from the project you have stayed in — not the biggest one.
  2. 2Make sure it is merged and has shipped in a release or a release branch.
  3. 3Write down the root cause, the approaches you rejected and why, and what a reviewer caught that you missed.
  4. 4Build a 5-minute talk: the problem, the investigation, the fix, the review.
  5. 5Present it at a club session and take questions.
  6. 6Submit the PR URL and write your reflection as your interview answer.
Done when
  • Merged, and shipped in a release or a release branch
  • You can explain the root cause, the approaches you rejected, and what a reviewer caught that you missed
  • You presented it to the club in five minutes and took questions

Choosing the largest diff instead of the one you understand best. Nobody in an interview counts your lines.

Also answer: This is your interview answer. Write it as one, out loud, and time yourself.

The reflection, every time

Five fields, hard caps, filed within 48 hours of the review. The caps are enforced on submit, not suggested — a reflection nobody can read in thirty seconds is one nobody reads at all, including you, three months later, in an interview.

1What I tried100 words

The approach you took — including the one you abandoned before it.

2What broke100 words

The error, the wrong assumption, or the dead end. "Nothing" is not an answer; if nothing broke, the task was too small.

3What the reviewer saidverbatim

Paste the actual comment. Do not paraphrase — paraphrasing sands the lesson off.

4What I would do differently60 words

One concrete change to how you'd approach the next one.

5The numbersone line

Hours spent · rounds of review · status (merged / open / closed).

This is not a course, and finishing it is not a certificate. Nothing here is completed by watching a video.