For the last two years, I’ve built software using pair programming. I recently switched jobs; during this process, I talked to quite a few colleagues and researched practices at many companies. I came to realize that, as rare as pair programming is, rarer still is the way in which we practiced it. When many developers are discussing pair programming, they mean something much less intense than what I have in mind.
My team generally paired for the entire working day during a sprint. Small amounts of code were written by solo developers (for example, people came in during different times in the morning or someone took a vacation day), but this was the exception. Our physical space and technology setup also supported this style of work. Each workstation drove two 27” monitors, two mice, and two keyboards. Our desks had room for a laptop on the side, which we used for tasks like email and research. At the beginning of most sprints, we switched the pairs so that each team members had the chance to work with every other member over time.
After working this way for two years, I want to reflect on the practice and share my thoughts. In part, I simply want to evangelize pair programming; I very much believe this is a great way of working as a team.
There are many perspectives on team building and team cohesion. I like Tuckman’s stages of group development: Forming → Storming → Norming → Performing. Performing is a great place to be as a team: we work together without (unneeded) conflict, we’re motivated, we believe in our own skills and those of our teammates, and we feel like the team as a whole is greater than the sum of its parts.
The question is: how does a team reach the performing stage? Many practices, such as retrospectives, contribute to this growth. But for our team, I think pairing is the biggest answer to how we successfully got there.
Performing requires that team members communicate well. The act of working together all day, every day, teaches this. Effective pairing requires a continual discussion. Through sheer practice, team members learn to communicate effectively. I would know, for example, that while working with one team member a concept might take a white board discussion, while with a different member the same thing might instead require sketching out interface signatures.
Another aspect of a performing team is understanding and appreciating each member’s abilities. Here, too, pair programming excels for the same reason. Writing code together, line by line, each developer learns very quickly the strengths and weaknesses of the other team members (as well as their own). Writing software as a cross functional team requires many things: programming language and library knowledge, experience with protocols, algorithm knowledge, building a continuous integration pipeline, writing and learning build tooling, operating system knowledge, writing deployment scripts and a myriad of other skills. Working directly together on each of these problems, I quickly learned my other team members’ strengths.
Trust is implicit in the definition of a performing agile team. A development team is always working toward a shared goal. Pair programming, though, takes this to another level. Every day, each developer is working directly with a second person to accomplish a specific goal. Over time, this shared experience built trust much more quickly than in other contexts I’ve experienced.
There are other advantages to this style of working outside of team building. One experience that I recall clearly is teaching something to the person I was pairing with. The next week, he taught it to the developer he was pairing with. Shortly thereafter, I heard the thing I initially taught spread to a fourth team member. Knowledge spreads very quickly among team members practicing pair programming.
Pairing spreads other types of knowledge too. In any code base, there will be examples of both the wrong and the right way to accomplish something. Chances are greatly increased when two developers write code together that at least one developer understands the proper pattern to use.
Code quality is a tricky thing to quantify, especially on a young product with a lot of churn. I believe pair programming greatly improved quality, but my evidence here is more anecdotal.
A quote by Phil Karlton comes to mind:
There are only two hard things in Computer Science: cache invalidation and naming things.
One common experience that jumps out at me over the last couple years of pairing is having a discussion around naming variables, methods, and classes. Over and over again, naming generated a genuine conversation. Does that method name actually convey what it does? Does that name match common patterns? Would extracting that expression to a named variable add clarity? Naming is very important to a codebase, and, in my experience, pairing helped to give it the attention it deserves.
Good tests are an attribute of quality code; going a step further, I do believe that test driven development produces better code. It can, however, be easy to slip out of the habit of writing a test first. Sometimes one can know the right thing but rationalize not doing it. It’s easy to tell yourself “I’ll write the test after”, or, on an especially bad day, “I can see the code is working, I’ll skip the test for this particular case.” Pairing helps to mitigate these kinds of things. Working with another developer, it’s harder to rationalize not doing the right thing.
Finally, pair programming helped me to maintain focus. While working in a more open space, distractions can abound. It can be also be easy to lose mental context when an interruption inevitably comes up and then have to spend minutes reloading the right elements into working memory (working memory in the human sense). I found that pairing helped me focus and, when I do lose focuse, to more quickly rebuild context. Pairing is a constant discussion, and this discussion forms a bubble that blocks out distractions. My auditory sense was actively engaged during development. I felt much less prone to distraction. Upon losing focus, rebuilding context went more smoothly; across two developers, we much more quickly picked something back up.
Pair programming helped our team build cohesion, it taught us to trust and communicate, and the practice disseminated knowledge and improved code quality. I firmly believe in the practice.