Priors

There's a lyric in a They Might Be Giants song:1

Now you're the only one here, Who can tell me if it's true: That you love me, And I love me.

The lyric is effective because it uses our expectations of rhyme scheme, parallelism, and reciprocity to have us all but certain that the final line of the verse will end with you. And the surprise serves a purpose: it uses the flouting of our expectations as a way of making us feel the potency of the narrator's self-centeredness.

The same thing happens when we read code: our expectations are set by familiar patterns. When something confounds our expectations, we will likely assume it serves a purpose. Sometimes our priors are so strong that—like an optical illusion—we see what we expect rather than what is actually there.

In practice, that means that a departure from common patterns may present in at least two ways:

  1. If the difference is something you notice but serves no purpose, you may become confused, because you assume the difference does serve a purpose.
  2. If the difference is subtle or your prior is strong enough, you may not notice the difference.

Departing from normal patterns for no reason is confusing.

Consider this code:

def do_that_thing
  return :thingy_1 if @foo.bar == :baz
  @blip.thingy_kind
end

I would expect this code to be written using a very famous thing called an else statement:

def do_that_thing
  if @foo.bar == :baz
    :thingy_1
  else
    @blip.thingy_kind
  end
end

or even

def do_that_thing
  @foo.bar == :baz ? :thingy_1 : @blip.thingy_kind
end

I also have the general prior that code should have as few execution paths as possible, and expect that short-circuit returns will generally be used to bypass significant amounts of code.

So the first code listing winds up taking some extra time to think through. “Hmm,” I think, “I wonder why this isn't an if/else. I must be missing something here. This isn't a short-circuit that avoids a big chunk of code. Is there something about the conditional that prevents it from working with an if/else? No. Why is it like this? I guess there's no particular reason. Weird. Wait, what was I about to do? I better check in on Slack.”

Doing something important in a subtle, unexpected way is dangerous.

I have seen this pattern several times:

def foo(my_arg)
  if (result = expensive_computation(my_arg))
    @my_orm.computed_value = result
  else
    MyLoggingBuddy.log_nobody_reads("It has all gone terribly wrong")
  end
end

My thinking here is “Okay, check to see if result equals expensive_computation. Wait, where is result defined? Is that a method call? It can't be a local. Looks like there's a bug here, too: that's a single equals sign. Oh, wait: is that intentional? Is this assigning the result of calling expensive_computation to result, and then evaluating result for truthiness? Ugh—I see why this is happening, and I guess I vaguely remember that it's an idiom in Ruby to use parentheses in an if statement if you're doing this, but that is super easy to miss.”

My strong prior is that it is an extremely common error to use = instead of == in a conditional. Another (albeit weaker) prior is that code will generally separate distinct kinds of operations—avoiding things like assignment within control flow.2 Here, things run so strongly against my priors that I may not even see what's going on: my eyes don't see that single equals sign the first time around.

If you do something obvious that differs from your reader's priors for no particular reason, your reader will likely assume it was for good reason and will waste time trying to figure out what that reason was. Even worse, if you do something subtle that differs from you reader's priors for an important reason, your reader may miss what's going on entirely, as we tend to pay little attention to something familiar.


  1. Kiss Me, Son of God, Lincoln (Bar/None Records 1988). ↩︎

  2. That is, unless language constructs exist to do exactly that, as in match and if let statements in some languages. ↩︎