Product Team Anti-patterns
And how pair programming can address them
Earlier in my career as a software engineer, I often underestimated my level of influence on the team. I saw myself as the “do-er”, not the “planner” or “coordinator”. Figuring out the best process for the team to follow wasn’t my job. Or at least that’s how I saw it.
Taking my cue from the other team members, I would lean on the product manager on the team whenever I had questions around this. The questions began seeping into how we wrote the software. Should we do the larger refactor now, or defer it? Should we write tests? Should we follow TDD for this work? Something didn’t sit right with me about this.
Then, one day a mentor of mine provided a powerful analogy:
Do you ask your manager permission to use a certain data structure, or to name a variable a certain way? Why then, are you asking permission to follow TDD?
This helped shift my mindset away from TDD being a means to “optionally improve test coverage” and towards a way of working that I adopt as a principle. It has been covered at length elsewhere about how TDD is more than just increasing test coverage. Michael Feathers’ talk on the synergy between testability and good design is a great example of this:
I am inclined to adopt a similar mindset towards pair programming — the more solo programming I do or encourage others to do, the more I realize that it creates and propagates anti-patterns on product teams.
In this post, I cover one of these anti-patterns, as well as how pair programming can counteract it.
Over the course of my career, I’ve onboarded onto dozens of new product teams both as a junior, intermediate, and senior software engineer. Every time, regardless of role, it’s been both an exciting and stressful experience. On the one hand, the existing team members are keen to see what I bring to the team. More cynically, they are eager to evaluate whether I’m up-to-snuff. In that vein, I’ve gotten past remarks such as:
“That’s a pretty basic query, don’t you have three years of experience in SQL?”
“As you’ve written production Rails apps before, Surely you won’t have an issue creating a basic Ruby class to encapsulate this logic.”
The key point here is that teams evaluate new members to find and fill knowledge gaps quickly. Sometimes, these gaps are not easily-fillable — when this happens, the team must accept that there may not be a fit on the team for the new member. The sooner the team can find this out, the better for everyone involved.
Many product teams (across different companies) have developed approaches that are ostensibly meant to onboard new team members quickly via a self-paced, independent onboarding process. Here are some examples:
Dev environment setup: “How fast can you get your development environment set up? The current record is 6.5 hours, held by Susan. She didn’t need to ask for help once!”
Grab something off the backlog: “Pull a bug off the backlog and get it to dev-done. Pick something straightforward so you don’t need to bother the other devs, as they’re busy with the currently-scheduled sprint. That way, you get an idea of our process, and you can show us that you can work independently.”
Poke around the codebase: “Play around with the app on your own and walk yourself through the codebase. This should get you familiar enough so that you can work on some features next sprint. Again, try not to bother the team with questions, and instead figure it out on your own by using the app. You’ve used our app before, right?”
In reality, these types of challenges screen for people who prefer to work on their own, and screen out people who are inclined to problem-solve collaboratively. Furthermore, it sets the expectation for the new team member that this is how the team works, and reinforces a pattern of maximizing “solo” time and minimizing collaboration time.
This an anti-pattern because it screens out exactly the type of people you need on a product team: collaborative co-creators and problem-solvers, as opposed to soloists who work best on their own and struggle with focused co-creation in a group setting.
Counteracting the anti-pattern with pairing
Let’s revisit how we can replay the earlier scenarios, but involve pairing so as to emphasize collaboration over independent work:
Dev environment setup: Instead of having the new team member do this on their own, pair them up with someone who has gone through the setup steps already. Use strong-style pairing with the new team member filling the role of Strong-Navigator — pairing up this way can expose comprehension gaps in the setup documentation. At the end of the session, reflect on and discuss how the dev environment setup process can be improved. Examples of questions to ask:
Does the documentation need to be updated?
Are there defunct libraries that can be removed?
Can more of the process be scripted?
Grab something off the backlog: Instead of doing this, have the new team member pair up with other team members on actual items being worked on that week (or sprint). Strong-style pairing with the new team member filling the role of Typist can be effective here, as such an arrangement does not require for them to have any familiarity with the codebase beforehand. At the end of the pairing session, the pair can reflect on any gaps that the new team member may have, and how to address them (e.g. “I’ve never used Rails serializers, I’ll spend some time learning more about them”)
Poke around the codebase: Instead of setting vague expectations and telling the new team member to do this, plan for the whole team to work as an ensemble and include the new team member in the regular ensemble rotation. Ideally, the team is already spending some of its time working this way; if that is the case, the team can just ramp up on those sessions over the course of the new team member’s onboarding period.
What about evaluation?
As mentioned, when a new person joins the team, there are two main concerns that the team has about the new team member:
How do we ramp them up as quickly as possible?
How do we evaluate that they’re a fit for the team?
It’s not hard to see how a pairing-centric onboarding process will improve the first point. But what about the second? If we give them onboarding tasks to complete, we can evaluate their technical aptitude based on how well they complete those tasks. If they pair on everything, then completion of the task tells us next-to-nothing — for all we know, their pairing partner could’ve done all the work.
Fortunately, there’s a straightforward solution to this. If you’re a lead who is tasked with evaluating whether the new team member is a good fit for the team, simply collect feedback from everyone with whom the newcomer has paired with since joining (ideally, this is every single team member). I find this to be a much more comprehensive and reliable way to evaluate the new team member than looking at how quickly they set up their development environment, or looking at some initial “solo” work they completed for the team (that will likely need rework anyway).
Pair programming continues to be a grossly under-leveraged tool for product teams. This tool can be effective at counteracting harmful anti-patterns that undermine a product team’s collaborative capabilities. I’ve covered one such anti-pattern in this post; in subsequent posts I cover a few more. These include:
Heroism: how heroes can jeopardize a product team’s long-term sustainability
Silo-ball: the tendency for silos across disciplines to emerge within product teams, and how this creates unneeded bottlenecks that make the team more susceptible to spikes in cycle time
Async Fixation: when product teams insist on only interacting asynchronously, leading to unnecessary increases in cycle time and shallow collaboration
I hope that the “(fend-for-your)self-onboarding” anti-pattern covered in this post has resonated with you; if not, perhaps one of the anti-patterns covered in future posts will resonate. Either way, I invite you to share your thoughts via a reply or comment.