Pair programming has a long and respectable history. None other than Frederic Brooks of Silver Bullet fame practised it as early as 1953. In terms of seniority it makes Scrum, CI/CD and BDD look like recent fads by comparison. But where many teams practise the latter with varying degrees of commitment and consequent success, pair programming has never been popular in any team that I ever worked in. From personal experience I am not surprised. I must have racked up no more than a week of solid pairing during 22 years as a developer, which was more than enough to my taste. What’s more, I wasn’t convinced of the alleged benefits.
There’s a slightly unsavoury intimacy about using the same keyboard, mouse and monitor that doesn’t work for me at all. I don’t like touching other people’s equipment (definitely no pun intended), and I certainly don’t want them manhandling my keyboard or mouse, pointing their finger at and even against my screen – the horror! Covid has at least backed me up on these germaphobe hang-ups, but it’s more than simple hygiene. I am picky about my peripherals and often spend my own money on the best quality there is. A crappy company-issue USB wired mouse is beneath me. My development environment is set up just the way I like it, with custom key shortcuts that don’t make sense to anyone but me. It’s not set up for other people’s convenience. I like to think I am getting more easy-going since reaching middle age, but I draw the line when it comes to my workstation.
Now these physical barriers needn’t be a deal-breaker in themselves. You can swap keymap configuration in your IDE easily. You can attach two keyboards and mice to the same workstation – for which you’ll need a larger desk, though. You can each stay at your own workstation and use a feature like IntelliJ’s code-with-me plugin. Physical distance doesn’t matter anymore then. Yet no physical aid can make the practice any more appealing to me, and that’s because of the mental challenge that working on the same piece of code together imposes on your brain. The way we tend to code doesn’t scale well to more than a single brain.
You’re not peeling a sack of potatoes
The school of pair programming tells you to switch roles between writing the code (the driver) and observing/reflecting on what’s being written (the navigator). Why do they insist on that division? It’s not because using the same mouse and keyboard at the same time is cumbersome: there are ways around that. No, the reason we need to take turns in these distinct roles is because of the mental processes involved in writing code. You can’t coordinate two brains on producing a single line of code. Peeling a sack of potatoes is perfectly scalable as long as everyone has their own knife. Coding is nothing like that. It is a thought process that has to manifest itself through a clumsy ancient QWERTY keyboard layout until Elon comes up with a brain implant. The typing itself is an overrated skill. When I told my neighbour in Rotterdam that I briefly worked as a translator he replied: Isn’t that just re-typing something in a different language? I didn’t argue with him, us being next-door neighbours.
Any code I type starts as an extremely ‘drafty’ version. I arrange and re-arrange my thoughts through the code on my screen. I’m sorry, but modern IDEs and strongly typed languages make all this far too easy. You can’t expect me to have a complete mental image of the program in my head that my fingers instantly turn into a bug-free executable. There is nothing linear about the process, in fact I’m all over the place. I type something and throw it out a minute later when a better alternative pops into my head. I can’t mull these alternatives over in my head and then write down the perfect code. I need the keyboard for that and don’t want anyone looking over my shoulder. I certainly don’t want to explain myself verbally each step of the way when I tend to change course a minute later. This can’t be much fun for the other party. They have to sit in forced silence watching me the driver spouting ideas and scrapping them until it reaches a state that I feel I can invite their opinion about.
It doesn’t have to be like this. After all it is called pair programming, not pair coding. The two are not the same thing and many others have made the same comment in the context of pair programming. Coding is an iterative stage within the much broader discipline of programming, let alone the entire software development lifecycle. Yet we tend to equate the two as if coding is all that matters. I really don’t understand why the development community at large has become so dismissive, even scornful, of up-front design. We take a business requirement and jump straight to code, working towards an acceptable version in increments and hoping an elegant design emerges as we code. This might just work but coding like this is doomed to stay a solo exercise, for all the reasons I just explained. The older proponents of pair programming very likely came to the table better prepared, with more detailed specifications and designs. Their Emacs wasn’t loaded with all the refactoring goodies we take for granted. The mental process to arrive at working code must have been much more structured and therefore suitable to do in pairs. That makes all the difference.
Allow me to segue into my hobby of writing stage drama for a comparison. A stage play consists of dialogue and stage directions for the actors to interpret and perform, much as lines of code are also interpreted by a virtual machine. Writing the dialogue usually takes up less than half of the entire creative process, although it is 100% of the finished product. A dramatist has to think deeply about plot and characters, make notes and even write biographies of the imaginary people in the play. There is a painstaking iceberg of creativity under the water. Even so, you go through several revisions of the dialogue. You can always make a phrase more poignant, more concise or more informative. The same goes for iterations of writing code. Even with more design up-front your code needs to go through revisions. But the changes will be smaller and less sweeping. There are still many ways to Rome, but the journey will have fewer unexpected detours.
Two pairs of highly paid eyes
I believe that our habit to design as you go along is the biggest roadblock to successful pair programming. It makes it an unappealing if not impossible option. Yet this straight-to-code approach is a habit you can change. Why not first gather your thoughts in pseudo code and jot down some free-format diagrams? More explicit design becomes crucial when you want to work in pairs. The results will be a better mutual understanding of the task at hand when you start coding. However, if the two of you have made a good, detailed design of the code-to-be it begs the question whether it is still worthwhile to have two highly paid pair of eyes during the actual coding stage just to avoid implementation errors. I don’t think it’s worth it. In the olden days of waterfall rolling out new versions of code was vastly more expensive compared to a well set-up CD pipeline in a cloud infrastructure. First-time right actually made sense back then.
I can do without pair coding. I am not convinced that the quality gains outweigh the considerable extra cost, and I am quite sure I don’t need it to feel happier in my job. But I would welcome a lot more collaboration in the specification and design stage. There are always requirements to software. The problem is we often don’t make them sufficiently explicit. Likewise, there is always a specification and an architecture, whether or not you put it in writing. Figuring it out from source code is exhausting and painful. Fleshing out the requirements, solving ambiguities, eliminating assumptions: all these had better be addressed before coding. The good news is that they are much, much better suited to a pair treatment than coding. A group of three or four will also work fine. This is the stage to ask questions, find answers and get everyone on the same page. This is where you discuss and define your APIs and data/transfer models, because they are more design than implementation. This is where you write your Given/When/Then scenarios in plain English for behaviour driven design. The group setting is where you address anything controversial and reach consensus on the direction to take.
Design and review together, code on your own
Implementation in working code will be quicker and less contentious after that. You have a detailed grasp of the requirement and any serious differences of opinion as to the implementation should have been talked through and resolved beforehand. After designing together, the pair or trio parts ways and implements the code on their own. Later they review each other’s work thoroughly, a practice that many teams already find valuable. An added benefit here is that the reviewer will have been involved in the design of the code, which guarantees better understanding, fewer surprises and higher quality of this vital review stage.
I must press one important parting thought. The approach I outlined is not a return to comprehensive up-front design. Design should be detailed, but each design session is concerned with a manageable chunk of work representing no more than a day for each developer. In practice that means getting together every day for an hour to 90 minutes and basically create software without writing code. I have tried it, and it works. Please don’t treat it as a meeting. It is actual work and can be really fun too.