Why this matters
Pair programming means two people work on the same code at the same time. One person types. The other reads, thinks ahead and watches for mistakes. The two people swap roles from time to time. The practice is used in classrooms, in coding bootcamps and on professional teams, and it is common enough that most engineers will sit beside a partner at some point in their career.
A beginner often expects pairing to be a way of splitting the work in half. It is not. Two people at one keyboard produce one piece of code. The benefit comes from two people checking each other's thinking as the code is written. The cost is that both people spend their time on the same problem. Pairing is worth it for some tasks and wasteful for others. This lesson shows how to make it work, what to say, and how to change roles without losing the thread.
The lesson does not teach how to find a bug, how to write a bug report, or how to read a program someone else wrote. Those have their own lessons in this module and in the debugging basics lesson of the VS Code module.
The two roles
The names come from a car, where the driver steers and the navigator reads the map. Most pairing guides use the same names, so you can use them with any team.
Driver. The driver has the keyboard and the mouse. The driver types the code, runs the program and makes each small change. The driver's job is to turn the plan into text on the screen.
Navigator. The navigator does not type. The navigator reads the code as it changes and keeps the plan in view. The navigator asks what the next step is, points out a line that looks wrong, notices when the goal is drifting, and keeps a short list of open questions.
The driver keeps the hands busy and the navigator keeps the eyes on the plan. If both people look only at the keyboard, the navigator is not doing the job. If the navigator types, the driver stops thinking about the code and only follows instructions.
A short example of the roles
Two students, Ana and Ravi, have to write a function that turns a title into a web address slug. A slug is the part of a web address that names a page, such as hello-world in example.com/hello-world. Ana is the driver and Ravi is the navigator. A short exchange might go like this. This is a made-up script to show the roles, not output from a program.
Ravi: We want lowercase words joined by dashes. What is the first thing the function does?
Ana: It makes the text lowercase.
Ravi: Good. Type that line. Then tell me what the next line should do.
Ana: Replace the spaces with dashes.
Ravi: Yes. Type it, then run the test file before we go on.
Notice what Ravi does. He does not write the line himself. He asks a question that makes Ana say the next step, and he holds the goal of the function in mind while she types.
Setting up a session
Before anyone types, spend two minutes on three things.
- Agree the goal in one sentence. For example: "make_slug turns a title into a slug with lowercase words joined by dashes." If you cannot say the goal in one sentence, you are not ready to type.
- Agree how you will know it works. Write the test first, or at least the examples. A test is a small check that compares the function's output with the output you expect. The test is the common ground. When the two of you disagree about whether the code is right, the test settles it.
- Agree who drives first. Choose by a simple rule, such as the person who wrote the last test, or flip a coin. Do not spend ten minutes on it.
Here is a test file for the slug example. Save it as slug.py. The title is the input and expected is the output you want.
def make_slug(title):
return title.lower().replace(" ", "-")
tests = [
("Hello World", "hello-world"),
("Hello, World!", "hello-world"),
("Python 3 Basics", "python-3-basics"),
]
for title, expected in tests:
actual = make_slug(title)
status = "ok" if actual == expected else "FAIL"
print(f"{status}: make_slug({title!r}) = {actual!r}, expected {expected!r}")Run it with python3 slug.py. The real output is:
ok: make_slug('Hello World') = 'hello-world', expected 'hello-world'
FAIL: make_slug('Hello, World!') = 'hello,-world!', expected 'hello-world'
ok: make_slug('Python 3 Basics') = 'python-3-basics', expected 'python-3-basics'The first and third cases pass. The second fails because the function only changed case and spaces. The comma and the exclamation mark stayed in the result. The test has done its job: it showed the problem in a form both of you can see.
The !r in the print line shows each value between quotes, so a space or a hidden character is easier to spot. You can read the output above without guessing.
Worked example 1: a session from a failing test
This example follows a pair through one fix. Ana is still the driver. Ravi is the navigator.
Start with the result above. Ravi says what he sees, without a solution: "The second line failed. The output still has the comma and the exclamation mark." Ana looks at the function and says the obvious next thing: "We need to remove the punctuation." Ravi asks a question that narrows it: "Which characters should stay?" They agree on the rule: letters and digits stay, spaces become dashes, everything else goes.
Ana types the new version. Ravi watches each line and does not take the keyboard.
def make_slug(title):
kept = []
for ch in title.lower():
if ch.isalnum():
kept.append(ch)
elif ch == " ":
kept.append("-")
return "".join(kept)ch.isalnum() is True for a letter or digit, and "".join(kept) joins the list of characters back into one string.
They run the same file again. The real output is:
ok: make_slug('Hello World') = 'hello-world', expected 'hello-world'
ok: make_slug('Hello, World!') = 'hello-world', expected 'hello-world'
ok: make_slug('Python 3 Basics') = 'python-3-basics', expected 'python-3-basics'All three pass. Ravi says "Before we go on, what else could the title contain?" That question is the navigator's job. It moves the pair to the next test instead of stopping at the cases they have already thought of.
Two things happened in this example that matter for any session. First, the test found the problem before anyone guessed at the cause. Second, the navigator named the problem without writing the fix. Ana found the fix herself and the pair both understood it.
Worked example 2: a disagreement about the rule
The second example is harder. The pair disagrees about what the right answer is, not about how to write the code.
Ravi tests a title with two spaces in a row, "a b". He adds the case to the test file and runs it. The real output of the four-case version is:
ok: make_slug('Hello World') = 'hello-world', expected 'hello-world'
ok: make_slug('Hello, World!') = 'hello-world', expected 'hello-world'
ok: make_slug('Python 3 Basics') = 'python-3-basics', expected 'python-3-basics'
FAIL: make_slug('a b') = 'a--b', expected 'a-b'Ana says the function is correct. Two spaces become two dashes, so the output is right. Ravi says the slug should have one dash, and that the expected value is a-b. Neither of them is clearly wrong. The question is what the program is supposed to do, and the code cannot answer that.
This is the moment the test pays off. The disagreement is not about who is smarter. It is about the rule. The pair writes the rule down. In this case they decide that a run of spaces counts as one separator. They put that in the test as the expected value, and then they can change the code. If they cannot agree, the right move is to ask the person who set the requirement, and to note the open question in the session notes. A pair does not decide a product rule by argument. It asks.
A useful habit: when you disagree, say the disagreement as a question about the expected output. "Should a b give a-b or a--b?" is easier to settle than "you are wrong about the spaces."
Switching roles
Switching is how a pair keeps both people engaged. A driver who types for an hour gets tired and stops reading the code. A navigator who watches for an hour loses the feel of the code and starts giving vague advice. Switching fixes both.
Switch at a clear point. Good points are these.
- After a test run, pass or fail.
- After a small change is finished and saved.
- When one of you has a clear next step to type.
- Every 15 to 25 minutes, if nothing else has come up. Set a timer if it helps.
Do not switch in the middle of a line or in the middle of a thought. Do not switch when someone is stuck and about to type something they have not explained.
How to switch:
- The driver says "switch" and stops typing.
- The driver reads the last line of the code and the last test result out loud. This is the handover. It takes ten seconds and it stops the new driver from starting in the wrong place.
- The two people swap seats or swap the keyboard.
- The new driver asks the new navigator, "What is the next step?" and waits for the answer before typing.
In the slug example, the switch might come right after the three-case test passes. Ravi, the navigator, takes the keyboard and types the next test case, "a b". Ana, now the navigator, reads the new test and asks what the expected value should be. This gives both of them the same part of the work from the other side.
Working with a partner who is not in the room
Many pairs work remotely. You share a screen or a shared editor, and you talk on a call. The roles stay the same, but a few things change.
- Only one person types at a time. If you both type, the code gets two sets of edits at once and nobody knows which one is in the file. Agree on who has the keyboard and say it when it changes.
- The navigator uses words more than gestures. "Line 4" is clearer than "this bit there." Say the line number, the function name or the value you mean.
- Keep a short notes file for the session. Write the goal, the decisions, and the open questions. Two lines per decision is enough. If the session stops halfway, the notes let you start again without reading all the code.
- Save the code in a version control system, such as Git, before and after each switch. The Git lessons in the foundations course cover how to do this. If you do not save, a change that broke the code cannot be undone easily.
Worked example 3: when both of you are stuck
The third example shows a session where neither person knows the cause of an error. This happens to every pair, and the question is what to do next.
Suppose Ana adds a line that uses kept before she creates the list. She saves and runs the file. The real output is:
Traceback (most recent call last):
File "/home/chiranjeet/dev/anisha/scratch-offtmp/claude-lessons-06/pair/nameerr.py", line 7, in <module>
print(make_slug("Hello World"))
~~~~~~~~~^^^^^^^^^^^^^^^
File "/home/chiranjeet/dev/anisha/scratch-offtmp/claude-lessons-06/pair/nameerr.py", line 4, in make_slug
kept.append(ch)
^^^^
NameError: name 'kept' is not definedThe folder part of the path depends on where you saved the file. The last line says what is wrong. kept does not exist at the point where the code uses it. Both of them can read that line. Ravi asks, "Where did we create kept?" Ana looks and sees that the line that creates it is missing. They add it back.
The pair's habit here matters more than the fix. Each person says what they see, in plain words, before proposing a change. Ravi does not say "I think it's the loop." He says "the error is on line 4, and kept has no value there." That keeps both people looking at the same fact. If the cause is not clear after a few minutes, the pair goes back to the test, shrinks the example, and checks one value at a time. The method for doing that is in the debugging basics lesson.
If a pair is stuck for more than about ten minutes on one error, a good move is to switch roles and let the other person read the error and say what they think it means. A fresh pair of eyes often finds the mistake faster than a longer look by the first person.
Common mistakes
The navigator takes the keyboard. This happens when the navigator sees the fix before the driver does. The navigator should ask a question instead, such as "what would you type next?" If the driver is truly stuck, switch roles on purpose and say so.
The driver stops listening. A driver who ignores the navigator is writing code alone while the other person watches. Try saying the plan aloud before typing it. If the navigator asks a question, answer it before typing the next line.
Both people talk at once. When two people speak together, neither hears the other. Let one person finish. A short pause before answering helps.
No goal is written down. Without a goal, the pair can spend an hour on code that does not solve anything. Write the goal before the first line. Look at it when you switch.
No test is run. A session that only reads code has no check on whether the code works. Run the test file after each small change, and say the result out loud.
Switching only when one person is stuck. A pair that waits until someone is stuck will switch late, when the stuck person is tired. Switch on a schedule, or after each test run.
Treating the navigator as a judge. The navigator is a partner, not an examiner. Point at the line and say what you see. Do not say "that is wrong" with no reason. "This line uses kept before it exists" gives the driver something to act on.
Ending without saving. When the session ends, save the code, update the notes, and write the open questions down. The next session starts from those notes.
Practice
Exercise 1: name the roles in a message
A teammate writes: "I'll type while you watch. If you see something odd, say it, but don't touch the keys." Which role is the teammate taking, and which role is the other person taking?
Show the answer
The teammate is the driver, because they type. The other person is the navigator, because they watch and point out problems without typing.
Exercise 2: write the goal and the first test
You and a partner must write count_words, a function that returns the number of words in a sentence. Write a one-sentence goal and three test cases with expected values. Pick the cases carefully. Include one with extra spaces.
Show the answer
A goal could be: "count_words returns how many words are in a sentence, where words are separated by spaces." Three cases could be:
count_words("one two three")should be3.count_words("")should be0.count_words(" spaced out ")should be2, because leading and trailing spaces do not make empty words.
The third case has a decision in it. Ask your partner whether leading and trailing spaces should count as words. The answer belongs in the test, not in an argument.
Exercise 3: decide a switch
Your session has run for 25 minutes. The last test passed. The driver has just finished the line that checks an empty input. Should you switch roles now, and what should the handover sound like?
Show the answer
Yes. A passing test and a finished line are clear points to switch. The handover is short: the driver reads the last line and the last test result aloud, for example "the empty string case passes, the output is 0." Then the two people swap, and the new driver asks the navigator for the next step before typing.
Exercise 4: fix a disagreement with a test
Your partner says make_slug("A B") should return a_b with an underscore. You think it should return a-b with a dash. What should you do before changing any code?
Show the answer
Do not change the code yet. Write the case as a test with the expected value that you agree on. First settle which value is correct. If you cannot agree, ask whoever set the requirement, and write the question into the session notes. Once the expected value is agreed, the test shows whether the current code matches it.
Exercise 5: read a test result out loud
Run the four-case slug test from worked example 2, or write your own with the same format. Read the result out loud as a navigator would. What do you say, and what do you not say?
Show the answer
Say what the result is, using the line itself: "The a b case fails. The output is a--b and the expected value is a-b." Do not say "the code is wrong" or "fix the spaces." Do not start typing a fix until the expected value is agreed. The navigator states the fact and leaves the decision to the pair.
Summary
- A pair has one driver who types and one navigator who watches the plan, asks questions and points out problems.
- Before typing, agree the goal in one sentence and write the test cases that show it works.
- Run the test after each small change and say the result out loud.
- When you disagree about what the output should be, settle the expected value first, then change the code.
- Switch roles at a clear point, such as after a test run, and begin the new turn by asking for the next step.
- Save the code and the session notes at each switch and at the end.