Why programming is hard for brains
The job of a programmer involves building new programs and changing existing programs.
In order to successfully work with a program, a programmer has to do two fairly difficult things. One is that the programmer has to learn and retain some number of facts about the program. The other is that, in order to understand existing behaviors and predict the effects of new changes, the programmer has to play out chains of logic in his or her head.
These two tasks involve what you could metaphorically call a person’s RAM (short-term memory), hard disk (long-term memory) and CPU (raw cognition ability).
The limitations of our mental hardware
When I poke around in a program before making a change in order to understand the substrate I’m working with, I might make mental notes which get stored in my short-term memory. Short-term memory has the obvious limitation that it’s impermanent. Six months from now I’ll no longer remember most of the stuff that’s in my short-term memory right now.
Other aspects of the program might enter my consciousness sufficiently frequently that they go into long-term memory. Long-term memory has the benefit of being more permanent, but the trade-off is that it costs more to get something into long-term memory than short-term memory. The cases where we can learn something easily and then remember it permanently are rare.
The other demanding mental task in programming is to look at a piece of code and run it in our minds. When I try to mentally run a piece of code, it’s like I’m combining my mental RAM with my CPU. I not only have to load some pieces of information into my short-term memory but I have to make a series of inferences about how those pieces of information will get transformed after they’ve had a number of operations performed on them. This is a harder task, kind of like trying to multiple two three-digit numbers in one’s head and having to remember each intermediate product to be summed up at the end. People tend not to be great at it.
If human memory were perfect and if our mental processing power were infinite, programming would be easy. But unfortunately our hardware is very limited. The best we can do is to make accommodations.
What if we had infinitely powerful brains?
If humans had infinitely powerful brains, programming would look a lot different.
We could, for example, read a program’s entire source code in one sitting, memorize it all the first time around, then instantly infer how the program would behave given any particular inputs.
It wouldn’t particularly matter if the code was “good” or “bad”. For all we cared, you could name the variables
c, and so on. We would of course have to be told somehow that
a really means
b really means
order and so on, but thanks to our perfect memories, we would only have to be told once and we would remember forever. And thanks to our infinitely powerful mental CPUs, it wouldn’t cost anything to map all the cryptic variable names to their real meanings over and over again.
But our brains are weak, so…
So we make accommodations. Here are a few examples of weaknesses our brains have and solutions (or at least mitigations) we’ve come up with to accommodate.
We can’t read and memorize an entire codebase
The best we can do is to understand one small part of a system, often at great cost. The solution? Try to organize our code in a modular, loosely-coupled way, so that it’s not necessary to understand an entire system in order to successfully work with it.
We easily can’t map fake names to real names
When a codebase is full of variables, functions and classes whose names are lies, we get confused very easily, even if we know the real meanings behind the lies. We can neither easily remember the mappings from the lies to the real names, nor can we easily perform the fake-name-to-real-name translations. So the solution (which seems obvious but is often not followed in practice) is to call things what they are.
We’re not good at keeping track of state
Imagine two people, John and Randy, standing at the same spot on a map. John moves 10 feet north. Randy moves 200 feet west. John moves 10 feet west. A third person, Sally, shows up 11 feet to the west of Randy. Now they all move 3 feet south. Where did they all end up relative to John and Randy’s starting point? I’m guessing you probably had to read that sequence of events more than once in order to come up with the right answer. That’s because keeping track of state is hard. (Or, more precisely, it’s hard for us because we’re not computers.) One solution to this problem is to code in a declarative rather than imperative style.
Broadly speaking, our mental weaknesses are the whole reason we bother to write good code at all. We don’t write good code because we’re smart, we write good code because we’re stupid.
- Our mental RAM, hard disk and CPU are all of very limited power.
- Many of the techniques we use in programming are just accommodations for our mental weaknesses.
- The more we acknowledge and embrace our intellectual limitations, the easier and more enjoyable time we’ll have with programming.