Logan Rios

December 7, 2022

We’re still sleeping on pair programming

Come on, folks. It’s been, what, like 30+ years since we were first formally kicking around this concept? How has this not taken off at every single software shop around the world? 

How we do it

The scary thing about working at a two-person company (sometimes three, depends on the week) is that I am the most senior of the developers at my measly ~3 years of experience. If you know me, you know that it's not actually that scary (for me), and that I actually like getting to try out new ideas and take the lead on big-picture decisions--but when I dig into the code by myself and tunnel on getting individual features to work, it's pretty hard to step back and constantly ask myself "what's best?" for the codebase. On the flipside, sometimes I open up Emacs and all I can think about is finding perfect, all-encompassing elegant solutions to the point where I'll write three lines of code then stare at the screen for an hour. You know how it goes.

While neither of these outcomes are strictly failure modes, they're also not the most conducive to shipping good product at a strong pace. So I enlist the help of my co-founder.

We usually block out four uninterrupted hours of time to do our work, either in-person or over a video call (I splurged on Discord Nitro only for this use case). It's been written about before, but the four-hour block seems to be the optimal amount of time for getting anything done, engineering-wise. We'll establish some main goals for the session and usually a stretch goal or two if we finish early, and hit any administrative topics--they might be called "blockers" in your corpo world--before moving onto the meat.

Never watch the master (for too long, anyway)

The first thing we do differently is: rather than swapping, we almost always have the more junior developer drive. Sure, the senior dev might show off some new feature, or give an overview of some part of the project structure, and maybe even do a lightweight prototype or demo of the feature we're targeting, but in my mind, it's very key that the junior dev do most of the driving.

Now, this sounds like hazing, or at the very least, unfair. But it's not without cause. Consider the following scenarios based on the difficulty of the current task:

Encountering a Trivial problem (Senior drives)
Senior: Okay, so we're just going to solve this problem using this random spurt of code, which is really easy. I'm not going to explain it in depth because it is so intuitive to me. How is your learning coming, Junior?

Junior: Zzz... oh, uh, yeah, that makes sense... zzz...

Encountering a Trivial problem (Junior drives)
Junior: Oh look, this is just like a problem I solved before! So, per our solution last time, I'm going to just implement it with this code here, which will do x and y by way of z. What do you think, Senior?

Senior: Wow, you're doing great! Now, maybe we can think about if this pattern could be generalized, or if maybe we could make it even more simple/readable?

Encountering a Good problem (Senior drives)
Senior: Alright, so, you may not understand this code here, but we'll need it to implement this feature. Line by line, it's supposed to be doing x, y, and z, but really I'm just going to spam it at the REPL until it works, and the entire solution could change by the time this is implemented, so I guess it might be hard to pay attention while even I'm still thinking through this...

Junior: Huh?! What does that even mean? Why did you think that one would work? Wait, you've already moved on?! Is this something I'll need to know soon? What is happening!!

Encountering a Good problem (Junior drives)
Junior: Oh gee, I'm not sure on how to do this one.

Senior: Well, let's decompose it into its smallest parts. Do you think you can solve just the x part of this problem, with pre-defined test data?

Junior: I might. Let's see... neat, that works!

Senior: Good job! Now, can we integrate it straight into y, or should we focus on solving y by itself first?

Junior: Wow, so many choices, but I sure am learning a lot about solving these types of problems!

Encountering a Hard problem (Senior drives)
Senior: Well, uh, this is out of both of our wheelhouses, so... just, uh, sit there and watch me look up documentation and spam stuff, I guess?

Junior: I'm not even sure I understand what we're trying to solve at all... *rolls eyes and checks phone*

Encountering a Hard problem (Junior drives)
Junior: Whoa. Why is this happening? What are we supposed to do about this?

Senior: That is a... really good question. Luckily, I just managed to repro. First, let's try to brainstorm methods to solve the problem. Then, we're gonna try some stuff that'll seem spooky at first, but I'm going to make sure that you know what's going on as you type.

Junior: I am grateful for this opportunity for us to grow together. Looks like we're in for a real adventure!

Why this works (for us)

Demonstrating with those mildly tongue-in-cheek examples above, both of us doing the pair work are getting big brain gains in addition to the features it necessarily implements. It also stops either one of us from "tunneling" too hard--it's pretty easy when you're the Observer to say, "okay, this seems like it's taking too long, let's 80/20 this sucker and move on". And, maybe most importantly for me, it has built-in accountability; you've promised another human being that you'll get something done within a set amount of time. It's a little harder to waste time faffing about on the internet when you're actively on a call or sitting there next to someone else.

Does this work all the time, for every feature? No, obviously not. Shipping software is often about more than just writing code (the "Peripherals" to-do list for our last cycle was only slightly shorter than "User Stories"), and that kinda stuff requires too many boring form-filling-outs, weird experimental sysadmin hacks, and endless project setup/maintenance that would get very boring for any Observer and highly frustrating for any Driver. 

But that's not to say pair work doesn't work well for most feature tasks. In our experience, it definitely does.

So why hasn't it caught on?

Primarily, I think it's because collaboration is not a natural state for most programmers. In general, we like to think of ourselves (at work) as siloed, brilliant little savants that use ninja skills and savvy tactics to solve any problem with lightning-speed, unburdened by the necessity to write maintainable code or, God forbid, explain ourselves to others. But that's, like, not really how most software development works, though.

Yeah, you probably can burn off items off your to-do list faster when you're working alone and not having to explain yourself along the way. But doing things that way also lets you pretend about some things that it's really convenient to pretend about:
  • You increase your job security by being "the only one that knows how that works"
  • You can pad your numbers/story points and blow off the extra time because you're not accountable in real-time to another person who understands what's happening
  • You can write sloppier code because peer reviews are often pretty shallow compared to someone else literally writing down your ideas
  • You won't get any pushback on your implementation because nobody will understand the problem well enough to challenge it

Is that a cynical take? Yes. But I'm generally pretty cynical about enterprise software development. I think most people write off pair programming because it's "annoying", and that "annoying" is just shorthand for the justifications I gave above plus-or-minus some natural expected asociality. So if you're optimizing for any of the justifications above, please, be my guest.

But, y'know, if you actually want to get things done, and teach people along the way, maybe Iisten to those old XP vanguards from the WikiWikiWeb, and do some pair programming more than once every other month. Just an idea.

About Logan Rios

shape rotation engine. bluesky