Step 13 of 24
Pair programming
Pair effectively as driver or navigator, run ping-pong TDD and strong-style sessions, pair remotely, and know when pairing pays off.
Pair programming is two people working on the same code at the same time, on one screen: one types, the other reviews and thinks ahead, and they swap often. Done well, it produces fewer defects, spreads knowledge across the team, and gets new engineers productive in days instead of months. Done badly, it's one person coding while the other watches their phone. This lesson covers the roles, the main pairing styles (including ping-pong TDD), remote pairing, the habits that make a session work, and when pairing isn't the right tool. Many companies also use a pairing session as an interview round, which the pairing interviews lesson covers.
Why pair at all
Pairing looks like it halves output: two salaries, one keyboard. In practice the cost is much smaller and the returns are elsewhere. Controlled studies (Williams and Cockburn's early experiments are the most cited) found pairs took roughly 15% more total person-hours than individuals for the same task, but produced code with noticeably fewer defects and simpler designs. Since fixing a defect after release costs many times more than avoiding it, the trade is often worth it on its own.
The bigger benefits don't show up in a single task:
- Continuous review. Mistakes are caught seconds after they're typed, not days later in a pull request when the author has moved on.
- Knowledge sharing. Two people understand every change. The "only Budi knows the payment code" problem — a low bus factor — fades.
- Onboarding. A new engineer pairing with someone experienced learns the codebase, tools, conventions and unwritten rules in a way no wiki teaches.
- Focus. It's hard to drift into social media when someone is sitting next to you, and two people unblock each other faster than one person searching.
- Better decisions. Having to explain an idea out loud exposes weak reasoning early.
The two roles
Driver. Has the keyboard. Focuses on the immediate tactical task: writing this function, making this test pass, getting the syntax right. Thinks out loud so the navigator can follow.
Navigator. Doesn't type. Reviews each line as it appears, and keeps the bigger picture: Is this the right approach? What edge cases are we missing? What's the next test? Keeps notes of things to come back to ("we should rename this", "what if the list is empty?") so the driver isn't interrupted constantly.
The navigator is not a passive observer, and not a backseat driver dictating keystrokes. Good navigation sounds like "I think we'll need to handle the empty cart case — want to write a test for that next?" rather than "type if len(items) == 0".
Swap roles frequently — every 15–30 minutes, or at every natural break such as a passing test. A pomodoro timer works well: swap at each break. Long stints at the keyboard turn pairing into a solo session with an audience.
Pairing styles
Ping-pong pairing
Ping-pong combines pairing with test-driven development, and is the most structured way to keep both people engaged:
Each person alternates between specifying behaviour (writing a test) and implementing it, so neither can coast. It also gives a natural rhythm for swapping, and a light competitive edge: write a test your partner will find challenging, but only the next thing the code needs.
Strong-style pairing
Llewellyn Falco's rule: "For an idea to go from your head into the computer, it must go through someone else's hands." The person with the idea navigates; the other drives. This is excellent for knowledge transfer — when an expert pairs with a newcomer, the newcomer drives, so they physically do everything and learn by doing, while the expert navigates at whatever level of detail the newcomer needs ("let's add a guard clause for the missing user" for a mid-level engineer; "type if user == nil" for someone on day one).
Unstructured pairing
Two people just work together, swapping when it feels right. It's the most common style and works fine between two experienced engineers who pair often. Between mismatched pairs it tends to drift into the stronger or more confident person doing everything, so add structure when you notice that.
Mob (ensemble) programming
The whole team — three to six people — works on one thing at one computer, with a driver rotating every few minutes and everyone else navigating. It sounds wasteful, but works well for hard, high-risk problems, for designing something everyone must understand, and for teams spreading knowledge quickly.
Making a session work
- Agree on the goal first. Two minutes on "what does done look like for this session?" prevents an hour of wandering.
- Think out loud whenever you're driving. Silence leaves the navigator guessing.
- Ask, don't tell. "What do you think about extracting this?" invites discussion; "extract this" ends it. This matters most when there's a seniority gap.
- Resolve disagreements with code, quickly. If you disagree on an approach, try the cheaper one for ten minutes, or write the test that would distinguish them. Don't debate abstractly for half an hour.
- Take breaks. Pairing is more intense than solo work. Most people can pair productively for four to six hours a day, not eight.
- Be kind about mistakes. Your partner will see you forget syntax and write bugs, and you'll see theirs. Psychological safety is a prerequisite: a pair that's afraid to look stupid hides its thinking, which defeats the purpose.
- Let the less experienced person drive more. Hands on the keyboard is where learning happens.
- Credit both people. Git supports multiple authors with a commit trailer:
Add surge pricing to fare calculation
Co-authored-by: Budi Santoso <[email protected]>GitHub shows both people as authors of the commit.
Pairing remotely
Remote pairing works well with the right setup:
- Shared editing rather than screen sharing alone, so both people can type without "can you scroll up?" — VS Code Live Share, JetBrains Code With Me, or tools like Tuple built for pairing.
- Good audio matters more than video: a decent headset, and a quiet room.
- Hand over control explicitly ("your keyboard"), since you can't see each other's body language.
- Use a shared timer for swaps and breaks.
- Hand over through Git if the tools fail: a short-lived branch and a quick commit-push-pull at each swap (sometimes called a "mob handover") works with any editor.
When pairing isn't the best tool
Pairing is a tool, not a religion. It pays least for:
- Trivial, well-understood work — renaming config keys, bumping dependencies.
- Exploratory research where you need to read docs and think quietly before you know enough to discuss.
- Tasks where both people would do the same thing and there's nothing to learn or catch.
It pays most for complex or risky changes, unfamiliar code, onboarding, debugging production issues, and anything only one person currently understands. Many teams pair by default on those and work solo on the rest, then use pull request review for solo work. When you pair, the PR review can be lighter, because the code has already been reviewed line by line as it was written.
Practice
Find a partner (a colleague, a friend, or someone from a study group) and run a 60-minute ping-pong session on this kata in Go or TypeScript: a Stack with Push, Pop and Peek, where Pop and Peek on an empty stack return an error, and a Max() that returns the largest value currently in the stack in O(1). Swap the keyboard after every test. Afterwards, hold a 10-minute retrospective. What questions should it cover?
Show solution
A useful pairing retrospective covers both the code and the collaboration:
- Did we both drive a similar amount? If not, why — and what structure would fix it next time?
- Where did we get stuck, and how did we get unstuck? Long stalls usually mean steps were too big.
- Did we disagree? How did we resolve it, and did we spend too long talking instead of trying?
- What did each of us learn from the other — a shortcut, a language feature, a way of thinking?
- What would we change next session: swap frequency, breaks, the tooling?
For the kata itself, the O(1) Max is the interesting design moment, and a good test sequence drives it out: push 3, push 5, Max() is 5; pop, Max() is 3 again. The naive "keep one max variable" fails that test, which pushes you to a second stack that records the maximum at each depth:
type Stack struct {
items []int
maxes []int // maxes[i] is the max of items[:i+1]
}
var ErrEmpty = errors.New("stack is empty")
func (s *Stack) Push(v int) {
m := v
if len(s.maxes) > 0 {
m = max(m, s.maxes[len(s.maxes)-1])
}
s.items = append(s.items, v)
s.maxes = append(s.maxes, m)
}
func (s *Stack) Pop() (int, error) {
if len(s.items) == 0 {
return 0, ErrEmpty
}
v := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
s.maxes = s.maxes[:len(s.maxes)-1]
return v, nil
}
func (s *Stack) Peek() (int, error) {
if len(s.items) == 0 {
return 0, ErrEmpty
}
return s.items[len(s.items)-1], nil
}
func (s *Stack) Max() (int, error) {
if len(s.maxes) == 0 {
return 0, ErrEmpty
}
return s.maxes[len(s.maxes)-1], nil
}Key takeaways
- Pairing trades a modest increase in person-hours for fewer defects, simpler designs, shared knowledge and faster onboarding.
- The driver handles the code at hand; the navigator reviews, thinks ahead and keeps notes. Swap every 15–30 minutes.
- Ping-pong pairing alternates writing a failing test and making it pass; strong-style routes every idea through the other person's hands.
- Agree on a goal, think out loud, ask rather than tell, settle disagreements with code, and take breaks.
- Remote pairing needs shared editing, good audio and explicit handovers.
- Pair on complex, risky, unfamiliar or knowledge-silo work; work solo on trivial or exploratory tasks.
Check yourself
Quiz
0/4 correct · 4 left
1.What is the navigator's main job?
2.In ping-pong pairing, what happens after Ana writes a failing test?
3.A senior engineer is pairing with a new hire to teach them a payment service. Which arrangement does strong-style pairing recommend?
4.Which task benefits least from pairing?
Concepts in this lesson
One-page notes on each idea, for review or a quick lookup later.
Finished reading?
Mark it complete to fill in your roadmap.