Why You Need at Least Three Sets of Eyes on Your Code

Nov 11, 2021 | Best Practices

Ranorex Selenium tool comparison

I know a better way to program software. I’ll explain why it’s better, and how you, too, can practice it.

Pair programming, a practice employed in numerous agile shops, provides a form of continual review. With pairing, two developers collaborate to build software. On the surface, this sounds like a wasteful use of expensive developer talent. But the notion that pairing doubles costs is only true to the extent that programming reduces to the physical act of typing.

Programming is a complex activity. This complexity easily overwhelms a solo programming head and yields results with numerous flaws. Two sets of eyes, though, in two programming heads, dramatically reduce the frequency of mistakes and lapses of judgment and eccentricities. When we bounce ideas off one another, we increase the likelihood we produce something with lasting quality and value. We create more flexible designs, more readily understood code that’s more easily maintained, and systems with fewer defects. The feedback provided by this two-headed interaction is active review.

After-the-fact reviews have been the more popular choice for attempts to boost software quality. Code walk-throughs, Fagan inspections and pull requests are common mechanisms for running what I’ll call “passive review after code,” or PRAC. Like active review, PRAC aims to mitigate the risks of low-quality software that solo programmers generate. The review in PRAC is, by definition, not active — reviewers instead examine things produced previously.

More importantly, passive reviewers examine things that were produced without their intimate involvement. In producing code with active review, you and I will have a detailed conversation involving things like assumptions and constraints: “We don’t have to worry about validating this message because the import process takes responsibility for it.” We’ll also dive deep into discussions about how to best solve that problem.

Unfortunately, not all the important bits of our dialogue make it into the code. The code becomes a distillation of the solution and loses a lot of the nuance about why. Even the dialogue directly represented in the code often ends up inscrutable.

To begin to understand such confusing code, we’d need to resurrect those conversations, but that’s not usually possible. We could ask for a supplementary document, but it would be costly to produce, unlikely to be kept up to date, and itself prone to misinterpretation anyway.

It’s better, then, that we insist a third set of eyes review the output from our pair programming. That review, moreover, needs to be sooner, rather than later. The code produced must be understandable, or else reviewers — and future maintainers — will waste time deciphering its intent. But will this independent review by a third set of eyes truly help?

The Problems With PRAC

Think of your own experience with PRAC. The first problem with PRAC is that it rarely uncovers more than surface-level defects. Sometimes it doesn’t even uncover what should be obvious, glaring problems.

The other week, scrolling through a bunch of past changes to code, I spotted a two-line code change that seemed not quite right. My pair partner told me that the change indeed contained a defect that was later fixed, but only after the customer discovered it. It turns out that three people should have seen the defective two lines: A pair produced them, and at least one other set of eyes reviewed their change afterward.

Yet “should be obvious” doesn’t mean we won’t gloss over two problematic lines in a sea of changes. While I knew something was wrong based on ingrained visual cues from seeing so much code, I had to reread the lines a few times for the reason behind the defect to sink in. Perhaps the core reason we miss these kinds of errors is that if we’re not deeply committed to the logic at hand, we’re not likely to think it as fully through as necessary.

The second problem with PRAC: We’d like to think that negative feedback from these reviews magically translates into better designed, more understandable code. But for too many reasons, this doesn’t happen.

The programmers who produced the solution we’re reviewing have this wonderfully human thing called pride. They arrived at the logic that supports the problem they were trying to solve using code, and it was probably the best solution they could derive at the moment. A suggestion involving a simpler solution is likely to be received with indifference at best, and viewed as insulting at worst.

As long as the software works, the pressure is usually on us to move on. It would be nice to replace an overly complex algorithm, but that rework requires considerably more of our precious time. The code is good enough for now; we must move on. “We’ll clean it up later,” we say, but that later never arrives.

Demand Commitment and Elevate Engagement

Most programmers, myself included, would much rather work on our own code (whether or not in a pair) than spend the time to review the code of others. PRAC represents yet another interruption; we must stop thinking about our code problems and solutions and temporarily think about someone else’s code. We’re reluctant to invest any more brain activity than needed, lest it chase away important thoughts about our own code.

Fortunately, there are a couple of simple mechanisms that eliminate the distraction of PRAC:

  1. If pairing, switch pairs mid-task.
  2. Mob.

Frequent Pair Switching

If we agree that a third set of eyes is important — and we should agree — then it’s best to involve that third set before we complete a solution. A pair usually will have decided on a course of action early in their session, and they are not likely to convince themselves to change direction.

A third set of eyes sitting down to actively complete a solution, particularly a set of eyes tied to an active mouth, has some chance of steering an errant approach back to a more sensible course. They’re now stuck working with this code, and, as such, they are far more likely to insist that the logic gets cleaned up to the point where it makes sense.

Such a switch doesn’t come without cost. Context-switching is hard, as I hinted earlier: we must drop our current line of deep thought and replace it with another. But we can get better at it, particularly when aided by focused, literate tests and readily digestible code. A modular design that is well-compartmentalized makes it easier to quickly ramp up on specialized knowledge.

But Isn’t It Still Too Late?

I’ve found pairing to be a good, sometimes great, practice, but I’m increasingly inclined to think it demands a bit too much overhead and management. It requires thinking about who’s not pairing with whom, when are we pairing, what happens when there are odd numbers, how do we deal with conflicts between person Y and person Z, and so on. And then there’s this context-switching cost I just mentioned.

A third set of eyes will still likely be met with some resistance, even if they arrive only 90 minutes after the prior pair got together to work. Granted, they have the right to now insist that the code they’re able to tackle at least makes sense. But that original course of action is probably protected by a solid amount of code — up to 90 minutes’ worth. A solution path surrounded by a mass of code has a stubborn way of resisting change.

Logical progression suggests that we might solve this problem by heading off any form of review after coding and just engaging the third party to join work on a solution from its beginning. Similar extrapolation leads to the notion that we may as well have everyone agree to the solution before it’s built. In other words, skip pairing and go straight to mobbing.

Mobbing in Brief

In Woody Zuill’s words, mobbing is “all the brilliant people working on the same thing, at the same time, in the same space and on the same computer.” It might sound like a ludicrous idea, yet it logically solves the challenge of PRAC, as well as the mechanisms needed to make pairing more successful. But everyone working on everything at the same time?

That’s a great discussion for another blog post. For now, I’ll say that I keep hearing from teams who mob that they go faster. I’ll leave thoughts about why that might be the case to you.

In the meantime: Yes, you want at least three sets of eyes on all code produced. It’s the minimum for any legitimate review process. To get those three sets of eyes, you can mob, but you can still choose to pair (or you can even choose to not pair and suffer PRACs).

Quality code is the key outcome we seek. “Three sets of eyes” is a good starter rule for achieving that outcome. The mechanism a team chooses can still be up to them.

All-in-one Test Automation

Cross-Technology | Cross-Device | Cross-Platform

Related Posts:

Secure Your Code, Harden Your App, Automate Its Testing

Secure Your Code, Harden Your App, Automate Its Testing

With DevOps practices more popular than ever in software engineering, there has been a push to integrate security, optimization, and frequent testing into the development process. However, as the baseline for what's considered good software evolves, so does the need...

A Guide to Test Driven Development (TDD)

A Guide to Test Driven Development (TDD)

For developers who work on many agile projects, test-driven development (TDD) may be something you wish to incorporate into your software development life cycle (SDLC). It’s a way to implement software programming while integrating unit testing, programming, and...