Inattention Blindness – Missing the Obvious
Your code isn’t working! You don’t know why and you’ve been staring at it for what seems like hours. You’re grumpy. You’re falling behind schedule. You’re getting increasingly irritated. Why doesn’t it work? Why can’t you see the bug? It can’t be so hard to find, can it?
Out of sheer frustration (as opposed to rational thought) you call over a coworker to help you find the problem. Within seconds they do what’s been seemingly beyond you – they identify the source of the issue. To make matters worse, it’s painfully simple.
You’re satisfied that the code works and your pointy-haired boss is relieved that the project is back on track. Life is back to normal and you’re cranking out more code. Yet despite this, one word festers in the back of your mind. One simple word: Why? Why could the coworker find the problem in no time at all but yet you couldn’t find it for an hour?
Take heart my friend. It’s likely something we’re all susceptible to, a problem in your ability to perceive and identify things. Let’s discuss this interesting phenomena, which you may not be familiar with: inattention blindness.
What’s Going On?
If you’ve not heard of it before, according to Wikipedia, inattention blindness (otherwise known as perceptual blindness) is:
… failure to notice an unexpected stimulus that is in one’s field of vision when other attention-demanding tasks are being performed … This typically happens because humans are overloaded with stimuli, and it is impossible to pay attention to all stimuli in one’s environment. This is due to the fact that they are unaware of the unattended stimuli.
There is a lot to the concept, so I’ll summarize it as best I can. Inattention blindness is a phenomena related to people’s perception of the data they receive through their senses.
Irrespective of something clearly being in plain sight, or in slang terms “right in front of their eyes”, people may not actually perceive that it’s there. They physically saw it, whether it’s a logic error, missing semicolon, or a missing statement. It registered in their conscious mind, but they didn’t seem to identify what was there (or that it was important enough to warrant their attention).
The end result? To them, effectively it wasn’t there.
The Invisible Gorilla Test
One of the most famous examples on the subject is the invisible gorilla test, which was conducted by Daniel Simons and Christopher Chabris. If you’re not familiar with the test, watch this video if it on YouTube. Subjects were asked to watch the video, which has two teams passing balls between each other, and count the number of passes by one team. What the subjects were not told is that during the video a person in a gorilla suit walks through the middle of the crowd, beats its chest, then walks off. You’d thinks this would be pretty obvious, right? The reports differ. Around 50% of the people didn’t indicate they saw the gorilla.
Strange, right? A person in a gorilla suit walks clearly in to view and struts around. How could anyone miss something so obvious?
Without going in to too much depth, there are a number of theories as to why. Scientists have theorized four possible reasons:
- Conspicuity: If an item is not particularly obvious or lacks meaning to the beholder, it may be missed.
- Mental workload and working memory: People can only consciously concentrate on a limited amount of information at a given time.
- Expectation: The gorilla is unexpected so it’s filtered out.
- Capacity: People only have a limited amount of ability to concentrate at a given time.
With so much in our lives and despite our experience to concentrate and focus on, it only makes sense that we have to filter out a large amount of stimuli, so we focus on the information which is both important and meaningful.
How Does This Apply to Programming?
Recently I read an interesting (and brief) paper, entitled What Makes Code Hard to Understand? by Michael Hansen, Robert L. Goldstone and Andrew Lumsdaine. The abstract is as follows:
What factors impact the comprehensibility of code? Previous research suggests that expectation-congruent programs should take less time to understand and be less prone to errors. We present an experiment in which participants with programming experience predict the exact output of ten small Python programs.
We use subtle differences between program versions to demonstrate that seemingly insignificant notational changes can have profound effects on correctness and response times. Our results show that experience increases performance in most cases, but may hurt performance significantly when underlying assumptions about related code statements are violated.
Take careful note of the latter part of the abstract:
Our results show that experience increases performance in most cases, but may hurt performance significantly when underlying assumptions about related code statements are violated.
How is it that the violation of underlying assumptions can negatively impact our ability to understand code? Well, I won’t regurgitate the paper. Instead I’ll review some points that it makes, discussing each in turn, in an open-ended fashion. Through doing so, I hope we can both identify some of the causes behind when we miss the obvious and how others, seemingly, aren’t encumbered with our blinkered vision.
Specific Language Experience / Programmer Expectations
Is increased experience a hindrance or a help? This one’s a little fuzzy. As we know more, do we make assumptions about situations and conditions such as what would and wouldn’t work, which more junior developers wouldn’t?
I’m sure you’re familiar with the situation where the more experienced person, because of all the knowledge and experience that they possess, will assume things will or won’t work. A new person completely unfamiliar with the subject matter might quickly make suggestions such as:
- What about doing it this way?
- Have you tried X?
- Did you think to do Y?
Ironically the increased knowledge has become a hindrance, not a help. Here’s a specific example; do we start to expect that code has to be written in certain ways, such as indenting with the K&R (Kernighan and Ritchie) versus Allman style?
As a result, we get so used conforming to conventions that when things don’t conform we have trouble reading them. I’m guilty of this. Are you?
Operator Overloading
In most languages, specifically PHP and JavaScript, operators can have multiple purposes or, when not used carefully, have unexpected results. This is fine if you’re used to it, but what if you’re not? What if you’re tired, stressed, or under the pump?
Take the following example (from the assignment operators section of the PHP manual):
$a = 3;
$a += 5;
$b = "Hello ";
$b .= "There!";
We can easily see that for $a
the result will be 8 and for $b
the result will be “Hello There!”. But what if we’d mixed up the operators as follows:
$a = 3;
$a .= 5;
$b = "Hello ";
$b += "There!";
What would the results be now?
Syntactic Noise
According to Martin Fowler, syntactic noise is:
extraneous characters that aren’t part of what we really need to say, but are there to satisfy the language definition.
He goes on to say that syntactic noise is bad because it “obscures the meaning of our program, forcing us to puzzle out what it’s doing.” Now, I agree with Martin’s observation that it is subjective, so likely you and I may wildly disagree.
However, this article on SourceForge points out that syntactic noise, partly introduced by complex syntax, cuts both ways:
Syntactic noise complicates maintenance of programs, and makes the learning curve steeper. However, it is important to note that complicated syntactic rules may make it easier for compilers to detect and report errors.
Let’s look at an example of “noisy” PHP. This was modified from the original Java example used by Paul W. Homer:
public function calcIndex()
{
$weightedSum = 0; $bondCount = 0; $status = false;
for ($i = 0; $i < count($this->bonds); $i++) {
$status = $this->yieldCalc($this->bondInfo, self::YIELD, true,
$this->bondFacts);
if ($status != true) {
throw new YieldCalculationException(
$this->getCalcError());
}
$weightedSum += $this->calcWeight($this->bondFacts,
Weight::NORMAL);
$bondCount++;
}
return $weightedSum / $bondCount;
}
If you’re familiar with all of the lexicographical structures, the above code is rather trivial. But to someone less experienced, it may take more time to decipher. Let’s step through it a section at a time.
$weightedSum = 0.0; $bondCount = 0; $status = false;
Here we’re initializing three variables: $weightedSum
, $bondCount
, and $status
, all in one line. It might be clearer if we initialize them individually on separate lines.
for ($i = 0; i < count($this->bonds); $i++) {
The for
construct was one of my favorites for some time. It provides an initializer, loop limiter, and incrementor. However, there’s quite a lot going on there and sometimes, in haste, we may not assemble it quite as we’d thought we had.
A simpler approach may be the foreach
instead, which would be:
foreach ($this->bonds as $item) {
The intent is much clearer to understand and it’s harder to introduce errors.
$weightedSum += $this->calcWeight($this->bondFacts, Weight::NORMAL);
$bondCount++;
Here we have a compact approach to variable assignment (+=
) and incrementing (++
). Yes, it takes less space, but is it necessarily clear or indicative? It’s up to you, but maybe the following might be clearer:
$weightedSum = ($weightedSum + $this->calcWeight($this->bondFacts,
Weight::NORMAL));
$bondCount = ($bondCount + 1);
As I said before, this is partly subjective, not to mention trivial. But hopefully you see that sometimes the constructs we use can both help and hinder us.
I’m sure we all know developers who think that the only way to code is to write in as compact and terse of a way as possible. Whether motivated by rock star status or job protection, who knows. But only very few people can read their code. When code has to be maintained over time, this isn’t the right approach.
Whitespace – Vertical and Horizontal
The paper indicates that when code is vertically clumped together, there is the expectation that the code is logically related. Take this example from ZendAuthenticationAdapterDbTable.php
in Zend Framework 2:
public function __construct(
DbAdapter $zendDb, $tableName = null,
$identityColumn = null, $credentialColumn = null,
$credentialTreatment = null)
{
$this->zendDb = $zendDb;
if (null !== $tableName) {
$this->setTableName($tableName);
}
if (null !== $identityColumn) {
$this->setIdentityColumn($identityColumn);
}
if (null !== $credentialColumn) {
$this->setCredentialColumn($credentialColumn);
}
if (null !== $credentialTreatment) {
$this->setCredentialTreatment($credentialTreatment);
}
}
Through a good use of vertical whitespace, we can see what code is logically related. We can readily identify the various conditions based on $tableName
, $identityColumn
, $credentialColumn
and $credentialTreatment
. Additionally, the code proper use of horizontal whitespace (indentation) gives the code a very logical and clear structure.
Owing to the logical structure of the code (yes I’m a sucker for well organized code) it’s simpler for the eye to follow the structure of it, keeping it readily in short-term memory.
Imagine if this was all clumped together with no grouping, indentation, or general sense of order. Would it be as easy to understand?
Conclusion
The examples in this article are deliberately trivial, but I hope you’ve not overlooked the essential points as a result. That is, often times the issues that we face in identifying problematic code can often be hidden from us, not because we lack skill, but because of little things like expectations, assumptions, or distractions.
When others are able to solve the issues that we’ve racked our brains to find, it’s not necessarily a poor reflection on us. They just have a fresh (or sometimes just different) perspective than we do.
So next time you’re beating yourself up for not finding a missing semicolon – don’t. It happens from time to time, and now you have a better understanding of why.
If you’re interested in this subject, I recommend the following resources for further reading: