Wrangling conditionals
There are few constructs in programming that impose more cognitive load than conditional statements. You’ve got to manage them carefully to keep things humane. Why is this?
It comes down to knowing the expectations of the reader, and then parceling out the cognitive load to meet those expectations. People read a method or function’s code to learn how it fulfills the contract expressed in its signature: OK, this does thing, how does it do thing? Therefore a codepath should meet the reader where they’re at; it should tell the story, the executive summary, of what the method does, succinctly.
To tell that story, a codepath should try to be concerned with what rather than how. So details that aren’t really saying what is going on should be pocketed away to the extent possible.
Break up conditional assignments with functions
An easy example is conditional assignments, when you conditionally assign to some variable. For example, this kind of thing:
if ($blah) { $thing = 'foo'; } else { $thing = 'bar'; } // $thing is used below
This is a trivial example, but the branches of the conditional could
be more complex. They could be long. They could contain intermediate
steps. Whatever the case, what we want to communicate here is get
$thing
, whatever it is. So let’s do that. Let’s put
the details here in a method or function:
$thing = $this->getThing($blah);
You’ve now separated the what (get the thing!) from the how
(depends on $blah
).
Aside from avoiding visual clutter, this also saves you the cognitive
load of tracking mutable state in your head. In the first example, we
have to do mental bookkeeping around the current value of
$thing
(plus whatever local variables you’ve introduced in
order to calculate $thing
). But now that you’ve got the
method call boundary, you can just write getThing
as a pure
function:
private function getThing(bool $blah): string {
if ($blah) {
return 'foo';
} else {
return 'bar';
}
}
getThing
doesn’t care where the value is going; main
codepath doesn’t care how it’s getting the thing; everyone wins.
Long conditionals
There are many similar situations that you ought to clean up for similar reasons. Another one is when you have a conditional that’s just long.
if ($blah) { // do thing one, with many, many statements // ... // ... } else { // do thing two, with many, many more statements // ... // ... }
It’s easy to reason about a conditional when you can see it all right there in front of you. However, once the branches of a conditional start to grow in length, it gets easy to lose the plot about where you are and what’s going on, especially if it doesn’t fit on one screen anymore.
The main reason is that when you come to a conditional, you sort of mentally push a stack frame. There are at least two ways control flow could travel, so it’s a decision point which you may have to return to later. Humans aren’t good stack keepers. Push more than one stack frame, and things get real tricky, real fast.
So this pattern is a good candidate for breaking the contents of each branch out into a function:
if ($blah) {
$this->doThingOne();
} else {
$this->doThingTwo();
}
This, again, separates the decision about what to do from the details of how those things are done, which can now be as long and as gnarly as you like. You’ve contained the complexity.