Is pairing for everybody?

Pair programming is a great way to share knowledge. But every developer is different, does pairing work for everyone?

Pairing helps a team normalise its knowledge – what one person knows, everyone else learns through pairing: keyboard shortcuts, techniques, practices, third party libraries as well as the details of the source code you’re working in. This pushes up the average level of the team and stops knowledge becoming siloed.

Pairing also helps with discipline: it’s a lot harder to argue that you don’t need a unit test when there’s someone sitting next to you, literally acting as your conscience. It’s also a lot harder to just do the quick and dirty hack to get on to the next task, when the person sitting next to you has taken control of the keyboard to stop you committing war crimes against the source code.

The biggest problem most teams face is basically one of communication: coordinating, in detail, the activities of a team of developers is difficult. Ideally, every developer would know everything that is going on across the team – but this clearly isn’t practical. Instead, we have to draw boundaries to make it easier to reason about the system as a whole, without knowing the whole system to the same level of detail. I’ll create an API, some boundary layer, and we each work to our own side of it. I’ll create the service, you sort out the user interface. I’ll sort out the network protocol, you sort out the application layer. You have to introduce an architectural boundary to simplify the communication and coordination. Your architecture immediately reflects the relationships of the developers building it.

Whereas on teams that pair, these boundaries can be softer. They still happen, but the boundary becomes softer because as pairs rotate you see both sides of any boundary so it doesn’t become a black box you don’t know about and can’t change. One day I’m writing the user interface code, the next I’m writing the service layer that feeds it. This is how you spot inconsistencies and opportunities to fix the architecture and take advantage of implementation details on both sides. Otherwise this communication is hard. Continuous pair rotation means you can get close to the ideal that each developer knows, broadly, what is happening everywhere.

However, let’s be honest: pairing isn’t for everyone. I’ve worked with some people who were great at pairing, who were a pleasure to work with. People who had no problem explaining their thought process and no ego to get bruised when you point out the fatal flaw in their idea. People who spot when you’ve lost the train of thought and pick up where you drifted off from.

A good pairing session becomes very social. A team that is pairing can sound very noisy. It can be one of the hardest things to get used to when you start pairing: I seem to spend my entire day arguing and talking. When are we gonna get on and write some damned code? But that just highlights how little of the job is actually typing in source code. Most of the day is figuring out which change to make and where. A single line of code can take hours of arguing to get right and in the right place.

But programming tends to attract people who are less sociable than others – and let’s face it, we’re a pretty anti-social bunch: I spend my entire day negotiating with a machine that works in 1s and 0s. Not for me the subtle nuances of human communication, it either compiles or it doesn’t. I don’t have to negotiate or try and out politick the compiler. I don’t have to deal with the compiler having “one of those days” (well, I say that, sometimes I swear…). I don’t have to take the compiler to one side and offer comforting words because its cat died. I don’t have to worry about hurting the compiler’s feelings because I made the same mistake for the hundredth time: “yes of course I’m listening to you, no I’m not just ignoring you. Of course I value your opinions, dear. But seriously, this is definitely an IList of TFoo!”

So it’s no surprise that among the great variety of programmers you meet, some are extrovert characters who relish the social, human side of working in a team of people, building software. As well as the introvert characters who relish the quiet, private, intellectual challenge of crafting an elegant solution to a fiendish problem.

And so to pairing: any team will end up with a mixture of characters. The extroverts will tend to enjoy pairing, while the introverts will tend to find it harder and seek to avoid it. This isn’t necessarily a question of education or persuasion, the benefits are relatively intangible and more introverted developers may find the whole process less enjoyable than working solo. It sounds trite: but happy developers are productive developers. There’s no point doing anything that makes some of your peers unhappy. All teams need to agree rules. For example, some people like eating really smelly food in an open plan office. Good teams tend to agree rules about this kind of behaviour; everyone agrees that small sacrifices for an individual make a big difference for team harmony.

However, how do you resolve a difference of opinion with pairing? As a team decision, pairing is a bit all or nothing. Either we agree to pair on everything, so there’s no code ownership, regular rotation and we learn from each other. Or we don’t, and we each become responsible for our own dominion. We can’t agree that those that want to pair will go into the pairing room so as not to upset everyone else.

One option is to simply require that everyone on your team has to love pairing. I don’t know about you: hiring good people is hard. The last thing I want to do is start excluding people who could otherwise be productive. Isn’t it better to at least have somebody doing something, even if they’re not pairing?

Another option is to force developers to pair, even if they find it difficult or uncomfortable. But is that really going to be productive? Building resentment and unhappiness is not going to create a high performance team. Of course, the other extreme is just as likely to cause upset: if you stop all pairing, then those that want to will feel resentful and unhappy.

And what about the middle ground? Can you have a team where some people pair while others work on their own? It seems inevitable that Conway’s law will come into play: the structure of the software will reflect the structure of the team. It’s very difficult for there to be overlap between developers working on their own and developers that are pairing. For exactly the same reason it’s difficult for a group of individual developers to overlap on the same area of code at the same time: you’ll necessarily introduce some architectural boundary to ease coordination.

This means you still end up with a collection of silos, some owned by individual developers, some owned by a group of developers. Does this give you the best compromise? Or the worst of both worlds?

What’s your experience? What have you tried? What worked, what didn’t?

Visualizing Code

When writing software we’re working at two levels:

  1. Creating an executable specification of exactly what we want the machine to do
  2. Creating a living document that describes the intent of what we want the machine to do, to be read by humans

The first part is the easy part, the second part takes a lifetime to master. I read a really great post today pointing out signs that you’re a bad programmer. Whether you’re a bad programmer or just inexperienced, I think the biggest barrier is being able to quickly and accurately visualize code. You need to visualize what the application actually does, what happens at runtime; but all your IDE shows you is the raw, static, source code. From this static view of the world you have to infer the runtime behaviour, you have to infer the actual shape of the application and the patterns of interaction; while closely related, the two are separate. Given just source code, you need to be able to visualize what code does.

What does it mean to visualize code? At the simplest level, it’s understanding what individual statements do.

string a = "Hello":
string b = "world";
a = b;

It might sound trivial, but the first necessary step is being able to quickly parse code and mentally step through what will happen. First for basic statements, then for code that iterates:

while (stack.Count() > 1)
    var operation = stack.Pop() as IOperation;
    var result = operation.Execute(stack.Pop(), stack.Pop());

Where you need to understand looping mechanics and mentally model what happens overall not just each iteration. Then you need to be able to parse recursive code:

int Depth(ITreeNode node)
    if (node == null)
        return 0;
    return 1 + Math.Max(Depth(node.Left), Depth(node.Right));

Which is generally harder for inexperienced programmers to understand and reason about; even though once you’ve learned the pattern it can be clearer and more concise.

Once you’ve mastered how to understand what a single method does, you have to understand how methods become composed together. In the OO world, this means understanding classes and interfaces; it means understanding design patterns; you need to understand how code is grouped together into cohesive, loosely coupled units with clear responsibilities.

For example, the visitor pattern has a certain mental structure – it’s about implementing something akin to a virtual method outside of the class hierarchy; in my mind it factors a set of classes into a set of methods.

public interface IAnimal
    void Accept(IAnimalVisitor visitor);

public class Dog : IAnimal { ... }
public class Cat : IAnimal { ... }
public class Fox : IAnimal { ... }

public interface IAnimalVisitor
    void VisitDog(Dog dog);
    void VisitCat(Cat cat);
    void VisitFox(Fox fox);

The first step in reading code is being able to read something like a visitor pattern (assuming you’d never heard of it before) and understand what it does and develop a mental model of what that shape looks like. Then, you can use the term “visitor” in your code and in discussions with colleagues. This shared language is critical when talking about code: it’s not practical to design a system by looking at individual lines of code, we need to be able to draw boxes on a whiteboard and discuss shapes and patterns. This shared mental model is a key part of team design work; we need a shared language that maps to a shared mental model, both of the existing system and of changes we’d like to make.

In large systems this is where a common language is important: if the implementation uses the same terms the domain uses, it becomes simpler to understand how the parts of the system interact. By giving things proper names, the interactions between classes become logical analogues of real-world things – we don’t need to use technical words or made up words that subsequent readers will need to work to understand or learn, someone familiar with the domain will know what the expected interactions are. This makes it easier to build a mental model of the system.

For example, in an online book store, I might have concepts (classes) such as book, customer, shopping basket, postal address. These are all logical real world things with obvious interactions. If I instead named them PurchasableItem, RegisteredUser, OrderItemList and DeliveryIndicationStringList you’d probably struggle to understand how one related to the other. Naming things is hard, but incredibly important – poor naming will make it that much harder to develop a good mental model of how your system works.

Reading code, the necessary precursor to writing code, is all about building a mental model. We’re trying to visualize something that doesn’t exist, by reading lines of text.

Reading code

Writing good code is all about making it fit for human consumption. Any idiot can write code a computer can understand, it takes care to write code another human can understand. But what does it mean to make code easy to understand? Programming is a literate task – writing well requires experience of reading code and in particular reading well written code. But how do we read code? Do we start at the beginning, read line by line until the end? Hardly.

Writing Code

Let me start by asking a different question: how do you write code? You probably write a test, write a small amount of code to make the test pass, then refactor to improve the readability, design etc of your code. Little by little the functionality accumulates; little by little the design emerges from a sequence of decisions and refactoring steps. TDD is fundamentally a design activity. Although you may have an idea of what your design will look like, the actual design will emerge from a sequence of small, interdependent activities.

Reading Code

Now six months later I’m reading this same code. What do I do? Well, I might start by reading the tests – well written tests should help me understand the intent of the code in question and, if they are acceptance tests, tell me what the customer thinks is important. In practice, I find this a painful way of figuring out the important things about a system. If TDD has been followed religiously you’ll have approximately 3.6 billion tests, grouped in a number of different ways making it difficult to keep enough context in my head at once to make any sense of the system.

Instead, I probably have something specific I’m trying to do. I have a change request or a bug to track down. So I probably dive in and make some guesses about where to start. I can’t remember the code so it’s a needle in a haystack. I probably miss and have to wend my way through the code trying to figure out how it fits together. I need to get some kind of big picture – at least for the part of the code I care about, as soon as I work out what that means.

Inferring Design

What I’m trying to figure out is the design of the software. How do these 200 classes relate to each other? What are the bigger patterns that will help me figure out where I need to look. Now, if I had documentation, I could look at that. But 1. it’s probably out of date or missing 2. even if it’s there, I wouldn’t trust it anyway.

So instead I find myself scribbling in a notebook – drawing class diagrams and other doodles with boxes, arrows and lines going all over. But how do I infer the design? I’m trying to understand how some parts of the system interact with each other. I look for references to interesting methods, chasing up the call stack to see what’s interesting. I find interesting classes involved along the way and I click through into interesting method invocations to see if anything fun’s happening. All the time I’m bouncing up and down call hierarchies trying to fit the system together.

As my notes start to coalesce I’ve got some idea about the key classes I care about and their roles in the system – I might find one or two key classes and scan through the whole thing, to see what other responsibilities it has. All the time following interesting method calls and looking for references.

The problem

Reading code is frankly nothing like writing code. But if writing great code means writing code that is easy to read, it’s a damn shame that the task of writing is so fundamentally different from the task of writing. It’s not as if after I’ve written a class I can pretend to have forgotten how it works and try and infer it. The best I can do is make sure that the code itself looks superficially clean. Is the style right? Is it formatted neatly? Insanely superficial stuff that frankly won’t make any difference in 6 months when I’m cursing the idiot that decided 16 levels of method calls with similar names was a sensible design approach (me).

The solution

On a superficial level I wish we could stop dealing with code as text and instead work on the syntax tree, with automated formatting following my preferences – not yours. Then we can all have our own idiosyncratic way of having code presented without having to argue for the billionth time whether spaces are preferable to tabs (of course they are) or whether curlies should be on a new line (of course not, heathen!).

The craftsmanship ethic of writing clean, well factored code is a good step. It doesn’t tackle the fundamental problem that reading code is different from writing it – but reminds us to address some of the symptoms: a comment is a whole new method just dying to be extracted. A 100 hundred line method will be at least twice as easy to understand refactored to two 50 line methods. Make things clear and simple and maybe the code will be easy to understand. Of course, I can still write a clear and simple mess.

It’d be nice if our IDEs were less damned text based. Maybe then we could have IDEs that give us a better, visual language to describe relationships between classes in. But it’s difficult to see how that would work, or how it would avoid degenerating into all the lousy CASE tools that already litter architects desks.

Ideally writing code would be more like reading code. We would be able to describe relationships natively, at a higher level than naming methods and classes. But what does that even mean?! How can I describe a set of classes without scribbling boxes and arrows? And more importantly, how do I make that a part of the implementation language so it never gets out of sync?