Thinking Abstractly: Why Programmers Don’t Write Binary Code

When you imagine a computer programmer at work, what comes to mind? Science fiction movies like The Matrix might give you the impression that programmers read and write endless streams of 1s and 0s, communicating with the computer in a digital dialect unintelligible to most humans.

But in reality, computer programming isn’t about the 1s and 0s, any more than painting is about the blobs of oil and pigment. When a painter works, their mind is focused on expressing an artistic vision for audiences to appreciate. Likewise, when computer programmers work, our minds are focused on expressing instructions for computers to perform. The 1s and 0s (binary code) are just the most raw, basic form of our medium, and we very rarely work in it directly.

Just like painters have invented new tools and techniques over the centuries, computer scientists have been working for decades to invent new ways of expressing instructions for computers. Where painters develop new brushes, paint formulas, and artistic movements, we invent and improve operating systems (like Unix, Windows, and Mac OS), programming languages (like Fortran, Java, and Ruby), and paradigms (like object-oriented programming and functional programming). Each new advance is based on advances of years past, as we build up layers of abstraction, allowing us to express instructions more easily, effectively, and economically.

Abstraction is a fundamental aspect of computer science and a valuable technique used by programmers every day, but it’s easy to understand even if you aren’t a computer scientist. To get a grasp on the concept of abstraction layers, let’s explore a common scenario unrelated to computers: walking to a restaurant.

An Everyday Example: Walking to a Restaurant

Suppose you want to meet some friends for lunch. They are waiting for you at the restaurant down the street and around the corner. What actions would you perform to get there?

If we think about it at a low level of abstraction, we could say that you perform some actions like these:

# Flex the muscles in your abdomen to lean forward slightly.
# Flex the muscles in your *right* leg to move it forward until it is in front of you.
# Extend your right leg until your right foot is on the ground.
# Extend your *left* leg to shift your balance onto your right leg.
# Flex the muscles in your *left* leg to move it forward until it is in front of you.
# Extend your left leg until your left foot is on the ground.
# Extend your *right* leg to shift your balance onto your left leg.
# Check whether you have reached the street corner. If you have, turn 90 degrees, then continue to action 9. Otherwise, go back to action 2.
# Perform actions 2 through 8, then go to action 10.
# Check whether you have reached the restaurant. If you have, stop. Otherwise, go back to action 9.

Even though you _do_ flex your muscles to bend and extend your legs when you walk, it would be very difficult to maintain a smooth stride if you had to think about each muscle movement individually. Thankfully, we can think more abstractly. Instead of specifying such minor details, let’s describe the actions a little more broadly:

# Step forward with your right leg.
# Step forward with your left leg.
# Perform actions 1 and 2 until you are at the street corner.
# Turn 90 degrees.
# Perform steps 1 and 2 until you are at the restaurant.

We can consider this an additional _layer_ of abstraction, because the ability to step forward is built on top of the ability to flex your muscles to move your legs. If we assume that you already know how to step forward by flexing your muscles, then there is no reason to write out the instructions in so much detail.

This new layer of abstraction makes the instructions easier to understand, and you could probably follow these instructions without looking like a real-life version of QWOP. But it’s still pretty awkward to think about each individual step you take. Let’s add another layer of abstraction:

# Walk to the street corner.
# Walk to the restaurant.

Now we’re getting somewhere! This is another layer because the ability to walk to a certain location is built on top of the ability to take steps. As you can see, there can be many abstraction layers built on top of each other.

Those instructions are pretty good, but we can go up to an even higher level of abstraction:

# Go to the restaurant.

This action is so abstract that we have actually just expressed our overall intention or goal. Even though you might accomplish this action by performing all those lower-level actions, there are definite benefits to thinking about actions and instructions (including computer code) at a higher level of abstraction.

h2. Why Think Abstractly?

Computer programmers think and write instructions abstractly because it allows us to:

* *Express instructions more succinctly and understandably.* We have already seen how abstraction can make instructions more succinct and understandable. Compare the one simple instruction (“Go to the restaurant.”) to the long list of complex instructions we started with. If you already know how to walk, it’s unnecessary and counterproductive to go into all that detail. In computer programming, if the programming language you use already provides the ability to calculate the sum of a table of numbers, for example, it’s usually a waste of time to write your own code to do that same thing.

* *Eliminate distracting details.* If you try to consciously control each muscle while walking, you’ll be so preoccupied with those little actions that you can’t think about anything else. Likewise, if programmers had to write directly in binary code, we would have a hard time thinking about anything except the immediate task at hand. By thinking abstractly and allowing the lower-level systems (the abstraction layers built up over the years) to take care of the details, we free up our mind to think about the overall structure and goals of the software we are creating.

* *Avoid needless repetition.* Notice how in the first walking instructions above, I had to repeat the instructions for each leg. Actions 2-4 are almost exactly the same as actions 5-7, except that right and left have been switched. As we added abstraction layers, the instructions became less and less repetitive. In computer programming, we have a principle known as DRY: Don’t Repeat Yourself. The fewer places you repeat some instruction, the easier it is to read and write, and the less work it takes to fix a bug or make the code more efficient later.

* *Use alternative solutions and implementations.* Suppose you were late for lunch with your friends, so you decided to ride a bicycle instead of walking. If we are thinking at the lowest level, we would have to totally replace many complex actions. But the higher we go, the fewer changes we would have to make. At our highest level of abstraction, we don’t require any changes at all: riding a bicycle is a valid way to perform the abstract action, “Go to the restaurant.” In computer programming, this type of abstraction allows one application to work on many different computers, even though each computer might have a different CPU, type of memory, etc. The application relies on the abstraction provided by the programming language, which in turn relies on the abstraction provided by the operating system, and so on down, layer by layer.

Of course, there are times when you need to give low-level instructions, such as when a certain action is not obvious or must be done in an unusual way. Likewise, programmers sometimes need to dive down into a lower level of abstraction to implement some low-level details that aren’t already provided, or to do something in a special way. But when we do that, it is useful to think of it as creating a new layer of abstraction upon which the higher-level instructions will rely.

h2. Abstractions All Around

Computer programming is certainly not the only area where abstraction layers exist. As we saw earlier, abstraction allows us to think about and express everyday actions, like going to a restaurant, without being distracted by little details.

Some other examples of abstraction layers in our everyday lives are:

* Workplace management, where a manager can delegate some task to an employee, without having to specify every detail of how the work is done.
* Government, where each state or province can have different local laws, which in many cases do not affect how the national government operates.
* Making a purchase at a store, where the staff doesn’t really care whether you pay with cash or credit card, as long as you complete the abstract action of paying.
* Even the concept of money is an abstraction that eliminates the distracting details of a barter system. (Next time you pay your electric bill, thank abstraction layers that you don’t have to negotiate in terms of goats and potatoes!)

Think about your own line of work and everyday activities. What layers of abstractions do you use and rely on? Have you ever devised an abstraction layer to simplify some task or process? Tell us about it in the comments.