Pairing and Feedback at Artium
A discussion with Liam Yafuso, VP of Engineering here at Artium, around his thoughts on pairing, feedback, and strategies to successfully introduce them to an organization.
Hey Liam, thanks for sitting down with me! So talk to me about pairing at Artium. What does it look like when we introduce pairing internally or on an engagement?
Of course! When we introduce pairing, we often get a mixed bag of responses. For people who have never paired, their initial assumption is that it’s a slower, less efficient process. Additionally, some people have tried pairing before, and a common response is that it doesn't work for them.
Pairing isn’t an individual experience; your success depends on who you're pairing with. If there is a lot of friction, it isn’t very compelling. However, when you pair with someone good at it, it allows you to work together contiguously for long periods and come out feeling refreshed at the end.
To pair with someone effectively, you have to have both social and pairing skills. When this happens, you feed on each other's energy, and it feels like you’ll never run out. Pairing is a more social, light-hearted way of working that also ensures you ask yourself and each other about the best way to build this software.
By the end of an effective pairing session, the number of feedback loop cycles you've had on the architecture is over 100. Every time you ask your pair, “What should we do here?” or “Is there a better way that we can build this?” you're getting what you would out of a pull request.
So what are the specific situations where you would use pairing?
I think there are two main situations where pairing is effective.
The first situation is knowledge transfer. If you're onboarding someone, or it is your first time working in a codebase, having someone there to explain the code and back up design decisions is crucial. This insight will rapidly accelerate the time it would take for someone to become useful in the codebase.
It’s difficult to read through the code alone to understand its purpose and why it was written that way. Knowledge transfer pairing is the closest thing, and sometimes the real thing, to having the specific person who designed the code explain exactly what they were thinking and the caveats. When you have someone to affirm the requirements that had to be met, you gain a new understanding of the codebase.
The second situation in which pairing is effective is when two experienced developers collaborate on a complex problem. Recently, I was working on the internals of a shopping cart implementation of an e-commerce application. These are core classes used by many different pages across various parts of the app. There were design and architecture decisions that we made that will have rippling effects across the system. It needed to be robust and well designed.
Pairing ensures at least two people check off nearly every decision made in a situation like this. This rigor will pay dividends later, as core decisions made early have ramifications throughout a project. Two brains are better than one for anything complex.
What would a situation be where pairing is ineffective then?
A situation in which pairing would be less effective is if we got a story like, “Round the corners on buttons and put them in the right margins from the top of the screen.” If you have two people doing something simple like that, one of them will be sitting there while the other one does all of the work. You should be soloing on something like that so the other person can do something more effective.
So what are some of the guidelines that lead to an effective pairing session?
I have two. The first is that you need a continuous amount of time to pair. I think a truly effective pairing session has a three-hour minimum. Pairing is social; it involves communication. You need time to chat, connect, and explain what you've been working on since you last checked in. Then, uninterrupted time is necessary so you can get into the flow and dive deep into the work.
The second thing is you need to reduce friction. To do this in a pairing session requires roles. It doesn't mean that one person has to play one role exclusively, but you need to understand the temporary roles you take on when you're pairing with someone. One person is going to be the driver, hands on keyboard, typing and writing code. The other person will be the navigator, suggesting ideas, pointing out typos, and being someone to bounce ideas off of. Having a regular cadence of switching roles will also give both people plenty of time in the driver’s seat.
Let’s say you’re experiencing some friction. Do you have ways to be able to ease that?
Two things I have found that help are; being comfortable rotating through roles and reflection on how the work is going.
If rotating through roles isn’t coming naturally, you can use ping pong TDD. This mechanism helps create clear delineations on when to rotate from driver to navigator. Ping pong TDD is where one person will be the driver and will write the test. They will then switch, and the other person will drive to write the implementation that gets that test to pass. Once the test is green, you switch back, and you can write the next test.
The other thing that helps when working through friction is to have a daily pair retro. Take 30 minutes at the end of every day, take a step back, and remove yourself from delivery mode. Put yourself into a place where you're not worried about trying to pump out the story, and you're simply chatting. Then, deliberately solicit feedback, both ways, about how it went. Ask questions like: “How did the day go for you?” “What part of this session felt the most difficult for you?” and “What part of the session did you find the most rewarding?” Create a safe space to solicit feedback regularly but be ready for painful feedback. It is essential to do this daily because feedback goes stale quickly.
The sooner you can address both frictions and successes, the more impactful that feedback will be. Even if it might be uncomfortable at the time, it's going to become more and more comfortable as you do it.
Feedback can be tricky; how do you handle the often uncomfortable nature of both giving and receiving feedback?
I think you should always start by finding alignment. When people take action based on an assumption, this leads to misalignment and then to friction. It may seem tedious, but you can align on each decision if you first find the lowest common denominator.
Off of this, you should structure your feedback around a common goal. For example, if there's a poorly written test, rather than pointing to all of the flaws, identify the gaps and ask what would cause them to write something this way? Feedback should begin as a conversation around a more abstract concept. Something like, “What makes tests good?”
Once you agree on those concepts and you gain alignment, go to that test and talk it through with that structure in mind. Ask if it meets the characteristics that we’re agreed on. If not, how could it be improved? At no point does anybody feel like there's a finger pointing at them. It's a collaborative exchange in which we're both focused on improving the code. There's no ego mixed in.
Pairing, collaboration, feedback, it's all about making sure people feel good about what they're doing, and nobody feels attacked. Strive to have everyone feel that they're in a safe place, and we're all trying to better ourselves and the code. That's the primary goal.
I think that’s helpful advice for a lot of aspects of life!
So then talk to me about how you typically see the relationship concerning pairing play out in our consulting engagements?
As a consultant, you're injected into an existing organization. So the first thing you want to do is gain allies with the locals. These people have deep knowledge of their systems that you don't have. You need people there who will help show you around and eventually evangelize the ideas you have.
The most effective way to do that is in a one-on-one setting where you can open up and have an honest discussion. I want to connect with them about their ideas and opinions and then have a successful pairing session. If someone has only had awful pairing sessions, I want to pair them with them even for a day. Once that happens, the table's turn. Together you write better code, and you feel better at the end of the day.
When people experience that feeling, there’s no denying pairing after that—everyone kind of buys in. When entering into a project as a consultant and working with their teams, I build relationships through productive pairing sessions. There’s a camaraderie that is created, and now you have a path forward.
You take those skeptics, who won’t give you the time of day, and you insist that they pair with you for one afternoon. By the next day, they spin around and tell their co-workers. You can make serious progress within the organization by taking the time and the effort to do this.
What are the things that you would like companies to know about pair programming going into an engagement?
The main thing I would love to eliminate is this misconception that pairing is slower. I want to disprove the false narrative that if you were soloing, you'd get twice as many parallel tracks done, and in turn, twice as much work.
The problem here is that collaboration is baked into the process for any decent engineering team. Typically, this takes the form of code reviews. When you factor in how long it takes for code reviews to happen, it's not that fast to have everybody on their own thing. If you think about the feedback cycle for your classic code review:
Very quickly, you can have an hour and a half of back and forth.
If you have people pairing, they can check the code in when it's done. Every piece of code, every line, has had at least two people reviewing it. You and your pair streamline story delivery. When you remove code review from your delivery pipeline, and as soon as the code passes tests, it gets merged, and it goes out. That's fast. All the teams that I've seen that were fast teams that delivered quickly were all doing pairing. They were bypassing code review.
So how do you ensure quality? Adding another person while you're writing code doesn't necessarily ensure quality.
That's an interesting question, but the reality is, it’s a question that goes beyond pairing, right? So even if we were doing code reviews and weren't pairing, the question remains. Just because someone else reviewed your code, how do you know that's ensuring quality?
You have to trust your people. If you have a bottleneck around code reviews, it's because the team has determined that only the most senior architect is allowed to do them. No matter how effective the team is, they can only deliver the code at the rate the one senior architect can read through and approve their code. So again, there’s a trust issue there.
It goes beyond code reviews though, how do you scale a team? The only way you can scale your team is through trust; you have to trust the developers and give them autonomy. That doesn't mean they're infallible, you want to have checks and balances in place, but pairing is precisely that. By having that second person, they'll tell you if what you have written is hard to understand.
If you believe that the engineers are inherently good at their jobs and trying to do the right thing, you should be comfortable with pairing. Even if you ensure a minimum amount of collaboration, that's a fairly high minimum. If you're developing something, another person is checking your decision-making and challenging your thought processes. If the code that you deliver still makes the cut at that point, it's probably high-quality.
Thank you for taking the time to talk to me! Any final thoughts?
If you make sure other people have a say in how something gets made and act as a community of capable individuals validating each decision, that's a pretty solid system. It's not that all code written this way will be the best code ever. It's that you're changing the threshold for the worst code. So together, we raise the minimum to ensure quality.