In this chapter
We'll make a first change in an unfamiliar codebase the professional way — a small plan, a branch, code that follows existing patterns, tests, a clear commit and a pull request — ship the live sales counter, and leave the repo better than we found it.
The Problem in Real Life
Twelve days to Sale Day. Anna has a map, a traced path, a running project and a debugger. John asks one last question before she types any code: "What's the smallest change that gives organisers a live counter?"
She thinks. "One small endpoint that returns a single number. The dashboard asks for it every ten seconds and shows it big. No new libraries, no live connections, no redesign." John nods. "Ship that. Make it fancy after Sale Day."
Leave it better than you found it.
John
One Big Clever Change vs. A Small, Safe, Reviewed One
Big changes to old code
Rewriting parts of an unfamiliar codebase two weeks before Sale Day is how outages happen.
No safety net
The tests were thin. A change without new tests is a guess.
Others must understand it
Reviewers have never seen this repo either. The PR must explain itself.
Making a Small Change, Testing It, Committing and Opening a Pull Request
The guest-room analogy: when you stay in someone's house, you can fix a dripping tap — but you don't knock down a wall. You fix one thing, tidily, in the house's own style, check it works, and tell the owner what you did. A first change in someone else's codebase follows the same manners.
- 1. Plan the smallest useful change: write the plan in two or three sentences before coding. Here: a
GET /events/:eventId/sales-countendpoint returning{ "sold": 4213 }, and a counter on the dashboard that polls it every 10 seconds (polling, Act 23 — simple and reliable; live connections can come later). - 2. Create a branch (Act 15):
git switch -c feature/live-sales-counter. - 3. Follow the existing patterns: copy the shape of the code you traced: a route with the same
requireOrganisermiddleware, a short controller, a service function with the same organiser filter, parameterised SQL. New code should look like it was always there. Small extra: a 5-second cache (Act 12, Act 14), so thousands of dashboards polling every 10 seconds don't each hit the database. - 4. Test it (Act 17): a unit test for the service (counts only paid orders for that event), an integration test for the route (200 with the right number for the owner; 404 for another organiser's event — the authorization rule, tested), and a check by hand in the running app with the debugger. She also fixes the skipped test she found in chapter two — leaving the safety net stronger.
- 5. Check the rest still works: run the whole test suite and the linter (
npm test,npm run lint) — the same checks CI will run (Act 19). - 6. Commit clearly (Act 15): small commits with clear messages saying what and why:
feat(sales): add sales-count endpoint for live counter,feat(dashboard): show live sales counter (polls every 10s),test(sales): re-enable skipped export test. Many teams use this "type(scope): summary" style, called Conventional Commits. - 7. Open a pull request (Acts 18, 24): a clear title, what and why, how to test locally (linking the new README section), screenshots of the counter, and what reviewers should look at carefully (the organiser filter, the cache). Respond to review, wait for CI to pass, merge, deploy (by the careful checklist this time, Act 19), and watch the logs and metrics after release (Act 19, Act 21).
- 8. Leave it better — the README: in a separate, small PR, she adds the repo's first
README.md: what it is, how to run it locally (chapter six's steps), the folder map, the entry point and request flow, how to run tests, and known issues (Node 16, manual deploy). Plus the corrected.env.sample.
| Step | What Anna did |
|---|---|
| Plan | One endpoint returning a number; dashboard polls every 10 s |
| Branch | feature/live-sales-counter |
| Code | Route + controller + service, same auth and organiser filter, 5 s cache |
| Tests | Unit (paid orders only), integration (owner 200, other organiser 404), fixed the skipped test |
| Checks | npm test, npm run lint — all green |
| Commits | Three small Conventional Commits |
| PR | What/why, how to test, screenshots, what to review |
| Better than found | Separate PR: first README + corrected .env.sample |
// routes/sales.jsrouter.get("/events/:eventId/sales-count", requireOrganiser, salesController.getSalesCount);// services/salesService.jsexports.getSalesCount = (eventId, organiserId) =>cache.wrap(`sales-count:${eventId}:${organiserId}`, 5, () => // cache for 5 secondsdb.query(`SELECT COALESCE(SUM(o.quantity), 0) AS soldFROM events eLEFT JOIN orders o ON o.event_id = e.id AND o.status = 'paid'WHERE e.id = $1 AND e.organiser_id = $2 -- same organiser filterGROUP BY e.id`,[eventId, organiserId]).then((r) => (r.rows.length ? { sold: Number(r.rows[0].sold) } : null)));// controller: if the service returns null (not this organiser's event) -> 404
it("returns 404 for another organiser's event", async () => {const token = makeDevToken({ organiserId: "org_other" });const res = await request(app).get("/api/events/311/sales-count").set("Authorization", `Bearer ${token}`);expect(res.status).toBe(404); // the query returns nothing for someone else's event});
Shipped: both PRs are approved the next day — the counter PR with one comment ("nice: a test for the other organiser's event"), the README PR with "Thank you. Finally." On a test event, Samantha watches the number climb from 4,213 to 4,260 while she talks. "That's exactly what organisers wanted." John gives Anna a thumbs-up from across the room.
The close: Anna pins the new README's first line to the board, next to the countdown: "# Organiser Dashboard — How to run locally." Ten days to Sale Day. Everything she's learned in eleven months is about to be tested at once.
Key Takeaway
Make a first change in someone else's codebase like a polite guest: plan the smallest useful change, branch, follow the existing patterns (same middleware, same authorization filter), test it at unit and integration level (including the security rule), run the full suite and linter, commit in small clear steps, and open a PR that explains what, why, how to test and what to check. Then leave it better — a README, a fixed test, a corrected .env.sample — in its own small PR.
Why This Matters
Your first change in a new codebase is how a team decides whether to trust you. Small, pattern-following, well-tested changes with clear PRs build that trust fast — and leaving documentation behind multiplies your impact far beyond the feature itself. This is the everyday shape of professional software work.
The counter is live, and the old repo finally has a README. Ten days later, it's Sale Day — and John has one last exercise for Anna before it: a repository she's never seen, and four things to find in it.
