Better Together: The Benefits of Pair Programming
I’ve got a new job where we do things a little bit differently than how I have in the past.
I honestly can’t think of any other company that I’ve heard of that routinely and systematically adopts pair programming paradigms. I imagine most of the companies I’ve worked for in the past have had accountants in the background saying something like “I don’t want to pay twice as much for solving a problem; why in the world have two people work on the exact same thing at once?”
This new workplace’s complete adoption of pair programming is a visible difference that really deviates from the norm in the job scene in my area.
For the purposes of this story, I define pair programming as having two people working on the same computer solving the same coding problem at the same time.
Don’t panic! While the “talking to another human being through most of the day” aspect may sound terrifying, it’s really not. In my experience, having two people working on a piece of coding really does prove beneficial in the end.
Sure, the more introverted members of this blog's readership (i.e., most of us) probably get a bit terrified by this prospect. But I promise, pair programming is worth it. The value that has come from having a pair to program with has really changed the way I see high-quality code.
Pair programming has made my code easier to read, reduced the overall size of my class files, and generally just simplified code that used to be way more complex.
Having learned the strengths of pair programming, I’d strongly encourage you to bring up the practice with your managers and team. The value of having someone else working directly with you on your code cannot be overstated. Sure, there are a few drawbacks; however, in my experience, they’re vastly outweighed by the net positive pair programming provides.
So, what is it that differentiates pair programming from flying solo?
The Physical Setup
In order for pair programming to work properly, the first and most important factor is the hardware setup. Pairs must literally share one computer.
The way we make this work is with two very large, very high-resolution screens, two keyboards, and two mice. The monitors are set into mirror mode, and each person gets control of a keyboard and a mouse.
That way, we can easily jump in as we have ideas we want to turn into code, without having to say, “Hey, can I have the keyboard for a minute?”
It’s truly coding in parallel. And it has all kinds of benefits.
Pair Programming Has a Cadence
If you walk into my office and don’t take much time to stop and notice what’s really going on, pair programming will probably just look like a whole bunch of programmers grouped in twos, with each pair sharing a computer.
It may not look a whole lot different than people programming by themselves, or like anything more than two people working on the same desktop.
But pair programming has a pattern and a cadence to it—it’s not just individual programming twice. If it were, all the value of programming as a pair would be lost and pairing up wouldn’t be an investment—it’d just be an added cost and a waste of one programmer’s time.
The general pattern of a pair builds heavily on a good routine of testing. And it depends on the pair to ensure the tests are well written.
Pair programming adds significant value by dramatically increasing the quality of the output we generate. The number of defects in the code we create is very small compared to in previous jobs I’ve worked on. And we rarely implement the wrong business logic because we always have to justify our logic to the person sitting next to us.
There are certain patterns that programmers can only achieve as pair programmers, and lately I have found myself really yearning for a second pair of eyes any time I write code—not just at work. (The unfortunate reality is I have to program by myself at home. But it’s OK—I make do.)
The routine we follow as a pair looks like this:
- Write a functional or integration test, write a unit test, write the code to pass the test, refactor the code, review the code, QA the code.
That probably seems pretty familiar, but every single one of those steps happens with two people at the keyboard(s). And the testing part really has a different feel to it, which I’ll cover in more detail a little later.
The one thing that makes pair programming a hard pill to swallow is the accounting cost associated with it. The non-tangible benefits of pair programming dramatically outweigh the cost of paying two developers to work on the same problem, but without appreciating how pair programming can hugely benefit the outcomes of a project, accountants will just see it as twice as expensive.
Instant Code Review
Real-time code review is one of the most obvious benefits to pair programming.
When you have two people actively engaging with the lines of code on the screen, more typos and logic errors get caught.
And if the code getting written doesn’t make sense for the problem domain it’s being used in, pairs are twice as likely to realize that the current direction of the code needs to change.
In fact, recently I was working with my pair on one of our cards. We were about an hour into it when he said something like, “I think another team already solved this problem.”
Turns out he was right: a different team had already written a library to do what we were trying to do. Which probably would have taken us the rest of the day to do. So we saved a ton of time by having two heads problem-solving.
Pair programming is the next evolutionary step in code review. If you’re not doing any kind of code review while you work on code, you should be. Almost all creative output benefits from review; this blog, for example, has editors—a whole staff of them.
Code review is just like having an editorial staff — it helps you catch things that you would otherwise miss. Because we’re human, and humans miss things.
Pair programming provides a real-time review of what you’re working on. Which means you’re much more likely to have two different people’s buy-in for every line of code.
If you believe every line of code is a business decision, then having two people make that decision together just makes way more sense.
Pairing and TDD
I’m not going to deep dive into the tenets of Test Driven Development (TDD) on this post because it’s a subject that deserves its own consideration.
Suffice to say that TDD means that you always have tests that don’t pass, and then you implement production code to make them pass. It’s where the term red->green->refactor comes from.
However, pair programming plays a crucial role in the success of thorough TDD. It’s the absolute best way to ensure your tests are testing the right thing — because your pair works to make sure that the business logic being tested is the right one. A test only has value if it tests the correct things.
In our pair programming setup, one person writes the automated unit test and the other person writes the production code to pass the test. Which opens up the doors for… dun dun dun… EVIL PAIRING.
When I joined my current team, I’d never even heard of the concept of evil pairing—in fact, I was so new to pair programming that the pattern of evil pairing was completely foreign to me. And then I got to be evil. And it was so much fun.
Defined succinctly, evil pairing is the practice of writing the smallest amount of code possible to pass a unit test, therefore proving the test incorrect or incomplete.
Even beyond how much fun it is, evil pairing has a really important function. It forces us to make sure our tests are just as good as our production code. Because without good tests, you really can’t have good production code.
The best way I describe this situation is to provide a contrived example of some test code and the evil way to make it pass. Thanks to Microsoft for giving me a decent baseline to work from.
You can see the evil way of passing this unit test in my gist here:
It’s obvious that a test for prime numbers shouldn’t fail if a value is anything other than one. Yet that’s exactly what I did—and the unit test passes.
That’s how I see evil pairing—the person implementing the code to solve the test should write the least code possible to pass the test.
We use this practice to expose edge cases or other scenarios the test hasn’t initially covered. (It also has the important fringe benefit that the person doing the implementation gets to evilly cackle when they pass a test in a horrible way.)
Sharing the Big Picture
As a general rule, neuroscience studies suggest our working memory can hold at most seven items at one time.
I don’t know about your programming projects, but in mine, there are almost always more than seven concepts to keep track of while I’m problem-solving.
If you add a second brain, however, that doubles the number of things we can keep track of at once. So now 14 ideas can be rattling around in your combined working memory.
Generally, it’s a good idea to have one person thinking about the most immediate problem at hand, while the other person tries to keep the big picture and the overall architecture of the system in mind. Often these thoughts overlap.
Sometimes they conflict with one another, and that’s when you have to have a conversation about the best route forward.
Usually, this conversation sounds something like a recent one I had about our data storage patterns. We weren’t sure whether we should use ORM or just plain SQL, so we talked about it for a little while and ultimately decided that plain SQL made more sense. This was despite the fact that we were using ORM for data storage at the time.
I was writing some ORM code, and my pair was thinking about how to solve the problem using SQL instead. The SQL made more sense. So that’s what we changed direction towards. Ultimately our code has had much less complexity and we have an easier time explaining it to one another, so it was a good win.
As a general rule, our teams consist of enough people to make at least two pairs of programmers at any one time. And we work in very close proximity to one another.
Generally speaking, everyone on the team works in pairs. On occasion, we may work by ourselves, but we value pair programming as a practice and strive to always incorporate pair working into our day-to-day routine.
We change pairs frequently so that we all get to work with one another on a routine basis. This also helps us create code that’s been written by a team and not just one (or two) people.
One thing that really helps this layout work is that we strongly believe in pair sovereignty. As one pair works diligently to solve a given problem, other pairs don’t interrupt that pair with suggestions like “it might be better if” or anything else that breaks the pair boundary. While you’re in a pair, your pair rules the code you’re working on, bar none.
This helps us stay focused on the problem we’re solving as a pair and not worry about what the other pair’s problems are. We’ll see it in code review, so there’s no concern we’ll lose track of business knowledge from a pair we’re not working with.
Dealing with Distraction
In an environment where your whole team shares a space and there are no cubicle walls (or actual walls) separating you from each other, it's easy to get distracted and veer off on tangents that have nothing to do with the problem you’re solving— or even with work at all.
In these cases, my team has a safety word that we use. If anyone on our team says the word “cinnamon,” it means that we’ve noticed we’ve been off-track as a team for several minutes and that the distraction has become unproductive in general.
Distractions are good; they help with team building and general camaraderie. But sometimes they get out of hand, so that’s why we can say “cinnamon”—as a gentle reminder it’s time to get back to work.
This kind of communication tool is important for any team layout. But with pair programming, there’s almost always a conversation happening. So we need a way to ensure that those conversations don’t distract us from our problem-solving goals.
Community Isn’t a Distraction
Even though we can sometimes veer off course and stop talking about the problems our team is solving, our team’s open-air communication builds better communication overall and acts as a safety net for our team. It helps us work better together and solve problems more consistently as a group.
Introverts Enjoy It Too
One of the big differences you’ll notice between pair programming and solo programming is the all-around focus on keeping people engaged with one another. It’s almost impossible to find a dark corner room or cubicle and hide from the rest of the team because we all notice—and care—when one of our team members isn’t available for pairing.
It’s not that we condemn the practice of taking a break—we all understand that we’re introverted people and need quiet sometimes. But we like working together. And we all understand that solving a problem alone can be very isolating and demoralizing. So we’re always helping one another any way we can.
In order to ensure we don’t end up with silos of knowledge where one pair has all of the information about a given solution, we also routinely do something called a pair switch. Either within a timebox or at logical breaking points (depending on what the team decides to do), we will regularly have one person stay at a machine, while the other one goes to a different pair.
This rotation helps ensure the whole team has context for the whole solution architecture, and it dramatically breaks down knowledge silos. It’s the best way I’ve ever seen of dealing with the problem of one person becoming the only subject matter expert in a domain.
Compare this with solo programming, where you could spend a day (or more) solving a problem and not really talking with anyone about the problem you’re engaged with.
Even if you take the time to do a post hoc code review of the code you’ve written, the rest of the team loses the problem-domain context that you gain in the moment, which can’t be recovered without a really involved code review.
If you’re going to have a code review to cover everything you learned solving a problem, it will probably take just as long as solving the problem in the first place. And most places won’t be willing to code review for that long.
So what happens in situations where you’re in a pair and your combined knowledge of the problem domain isn’t enough to get through the current task?
There’s one more tool that we have in our toolbelt, and it’s called “the turnaround.” That’s when we literally say the phrase “turnaround” to our team, and everyone turns away from their desk and towards the team to discuss the problem at hand.
Usually, it takes less than five minutes of turnaround discussion to come to a reasonable agreement as a team about how to solve the issue. On occasion, turnaround time can take quite a bit longer to come up with the correct path forward, but typically it’s pretty quick.
The other day, the topic of a turnaround was simply me verifying that we wanted to call our database table “Chewbacca.” I couldn’t remember, so I asked the team. It took about fifteen seconds. But that fifteen seconds saved us a ton of cognitive dissonance later if I’d just decided to call it “HanSolo” instead. (Obviously, names are changed to protect the codebase, which I’m not allowed to disclose)
Regardless of the timing, as long as the whole team understands the direction we’re taking with our code, the main goal of the turnaround is met.
Pair Programming is Fun
More than anything else, what you get as a pair programmer is a sense of having fun with someone else who loves programming as much as you do.
I’ve worked in places before where I put on my headphones, cranked out code for eight hours a day, and then went home. It’s an introvert’s dream—but it can also be isolating and frustrating.
Pair programming completely eliminates the isolation that our programming jobs create, on purpose. It encourages community and enriches our overall team experience.