Demystifying pair programming for novices
Over the past month, I have pair programmed with close to a dozen software engineers of varying experience and skill levels. For most, this was my first time pairing with them.
Through this experience, I have observed a few common themes. I have discussed these themes with the people I paired with. We found that these discussions led to a shared understanding which in turn resulted in more effective subsequent pairing sessions.
Furthermore, I have found that these themes apply regardless of pairing style (e.g. driver-navigator vs. strong-style) or context (e.g. working on a TDD kata vs. debugging a production codebase).
The three themes I cover in this week’s post are: control, communication, and group flow.
During a pairing session, exactly one person has control of the coding process at a time. In the traditional Driver-Navigator pairing style, this is the Driver role, who also happens to have the keyboard. In Strong-Style Pairing, however, the Navigator directs the Typist on what code to write. This means that the Navigator has control, while the Typist has the keyboard.
Recognizing the concept of control is important, because it is unavoidably present in a pairing session. It’s impossible for both programmers to have control at the same time, yet a healthy pairing dynamic means that control switches fluidly and frequently between pairing partners. I find that it’s beneficial to call this out in a pairing session and establish norms with your pair around it.
For example, one principle I employ is to agree that control should be given, not taken. I find that it’s very easy to take control when pairing virtually, especially when using tools such as VS Code Live Share and Tuple. While this might seem convenient the person taking control, it can be jarring for the one losing it. Switching control doesn’t need to be a formal thing. I find that simply asking, “mind if I try something?” is usually sufficient.
Having said that, teams and individuals who’ve paired hundreds of hours together may feel this notion of control a stilted way to go about pairing. That is completely fine and even expected for teams with a high degree of pairing proficiency. Nonetheless, I have found that “control” is an important concept to keep in mind when pairing with a new teammate, and especially someone who is new to pair programming.
In a past post, I explained how I initially struggled with pairing because of my inability to think aloud. Coding in silence meant that my pairing partner had no idea what I was trying to do until the code was written. This severely limited the level and amount of contribution that they were able to offer.
Concretely, they were only able to point out typos, syntax errors, and style inconsistencies — essentially everything a well-configured IDE already does for you. Consequently, they would lose interest and thus struggle to stay engaged during the session.
Thinking aloud for the person in control is only one part of communication, however. It’s also important for the person in control to be deliberate about how they ask questions to their pairing partner.
Before elaborating on this further, it’s important to explain two collaboration modes that come up when pair programming.
Actual coding takes a pause when a pair switches to “discussing mode”. Consequently, either person is invited to type out notes, pseudocode, or even sketch out some ideas and diagrams on a virtual whiteboard.
There are two important aspects of this mode. First: executable code is not written. Second: the mode should not last longer than a few minutes.
The other mode is “coding mode”. This is when executable code is being written. During this mode, it’s imperative that the person in control is continually thinking aloud. They should also be aware that asking a question may result in a shift to “discussing mode”, depending on what is asked, and how.
For example, if they ask a straightforward, closed-ended question (such as, “is there a native method for splitting a comma-separated string into an array?”), then the coding mode is maintained, as the pairing partner can provide them with a simple answer. I have found that having a REPL open to do so is a quick an effective way to answer these kinds of questions.
On the other hand, if an open-ended question is asked, this invites a switch from coding to discussing mode. This, of course, is critical — without a discussing mode, there is little point to pair programming in the first place. In most cases however, the discussing mode has rapidly diminishing returns when it lasts longer than a few minutes. In pair programming, the bias should always be towards writing working code, so it’s important that both pairing participants are aware of the mode currently in effect, and when it changes.
For example, the person in control may say, “I’m not sure whether to extract this to a class or a module. What do you think?”. Their pairing partner may respond with, “I’m not sure either. Instead of discussing it, let’s pick the one you’re more comfortable coding out and see where it goes.” In this situation, the pair agreed that switching to discussing mode wasn’t worth it, so coding mode was maintained instead.
If you don’t have control
If one doesn’t have control, there are a few counter-intuitive communication guidelines to keep in mind. When in coding mode, one communicates almost exclusively by answering questions as opposed to proactively making suggestions. This allows for the person in control to maintain an uninterrupted focus.
There is an important exception to this: when a test unexpectedly fails, and the one without control is able to see why. In that case, it makes sense to jump in and proactively make a suggestion, as the person in control has already lost focus due to the unexpectedly failing test (note that this exception doesn’t apply when the test expectedly fails, for example during the “red” step of the “red-green-refactor” cycle).
Let’s say that the pair is coding away in coding mode. What if the one without control has an idea they wish to discuss? Similar to discussing, the coding mode also shouldn’t last for too long. A pause in coding and consequential shift to discussing mode presents an opportunity for the person not in control to share their thoughts and discuss their ideas with their pairing partner. This is also a natural point at which to consider handing over control to the other person.
This third pairing theme underpins the first two. All experienced programmers are familiar with the notion of “flow state”, but most have only experienced it when soloing. I was convinced that flow state is something that simply doesn’t happen when pair programming. I later came to realize that one can in fact achieve this while collaborating with another person during a pair programming session.
I believe that recognizing and respecting control helps programmers achieve group flow. Further, establishing communication norms based on coding vs. discussing modes help maintain group flow. Finally, reflecting on the sessions themselves with one’s pairing partner helps optimize for group flow.
Beyond affording an opportunity to shift into discussing mode, working in pomodoros allows for pairs to reflect on their pairing experience. Here are examples of topics that could come up in doing so:
Did we give enough time for discussion in that last session?
I noted down some some ideas that I’d like to try. Can we discuss and perhaps you could give me control for the next pomodoro?
What else can we do to improve our group flow in our next session?
We coders just want to code. When we feel like we’re on a roll, we don’t want to be interrupted because being in a flow state feels good, and it’s fulfilling when we ship working code as a result. However, it feels even better when we include our teammates, both in the learning as well as the shipping.
While pair programming is the most effective way I’ve seen to include the team in this, it’s still an uncommon practice that feels strange and unusual to many programmers. Hopefully, the pairing themes covered in this post will help demystify it and thus make pair programming accessible to more software engineers.