Abstraction is the fundamental ability that powers the way computer scientists think through and solve problems. It is the most important tool in the computer scientist's arsenal and often the hardest skill to master.
It is also a very powerful tool for general-purpose problem-solving. The right abstraction can often unlock unforeseen solutions to otherwise intractable problems. In some rare cases, it even allows for fundamentally novel insights only possible by thinking at the right level of abstraction.
In this article, I will explain abstraction in the context of solving problems, why it is such a powerful tool, and finally, give a concrete tip on how to improve at it.
This article is part of a series on computational thinking called How to Think like a Computer Scientist. In this series, I explore techniques from the computer scientist’s toolset and how they can be applied by anyone to solve problems in any domain.
If you want to learn more, subscribe for free to get future articles directly in your inbox.
Abstraction in action
Abstraction is the process of taking a problem, situation, or concept in general, removing all irrelevant details, and leaving a simplified, more general version that captures what is truly essential to the thing being abstracted.
Abstraction is a form of purposeful simplification—but not one that stems from laziness or a lack of capacity. It's a meaningful simplification that makes a problem easier to solve but also more general and, thus, more relevant.
What is relevant or not about any given concept or situation is very contextual. Depending on the type of analysis you want, the same details may or may not be important.
That is, the why determines the what.
To see this in action, let's begin with a typical example from a typical computer science class.
An example problem
Suppose you want to solve the problem of getting as fast as possible from home to work. How could we solve this?
We first need to understand two things: what we are exactly asking for and what our decision space is, that is, what we can do.
For example, this problem is very different if you use public transportation or a personal vehicle. That is, the means by which you can move is an important detail, as it will determine your decision space. Can you travel through any street, and stop anywhere? Or do you need to pick from a predesigned set of routes and stop at one of the predetermined bus stations?
Likewise, the problem changes if we want to minimize total travelling time, fuel cost, time spent waiting at red lights, or the total number of swears thrown at cyclists.
Suppose you pick a goal: minimize total time spent on the road. Furthermore, assume you have a personal vehicle that can, in principle, travel anywhere in the city.
This is already an exercise in abstraction because we are deciding, for example, that the level of risk of a given road is irrelevant. We want the fastest route, not the safest. We could have done the opposite or somehow tried to find something in between.
The point is that deciding what matters immediately makes some otherwise important details irrelevant to this particular instance of the problem.
Making the problem tractable
Are we ready to solve the problem? Well, not yet. There are still a thousand factors that can determine how fast you can move around the city: traffic, semaphore lights, pedestrians, quality of the road... And worse, all of this changes daily, perhaps even every second! If we want to have a chance to tackle this problem at all, we'll have to make some further simplifications.
So, let's assume we can model each road segment as a straight line between intersections, with an average time cost associated. That is, we throw away all the details of a given road except for the average time it takes to traverse it. It doesn't matter how many lanes the road has or how bumpy it is. Also, it doesn't matter which time of day or year we happen to be.
This is a huge simplification, but one that makes the problem tractable with a very efficient computational algorithm—Dijkstra's algorithm, to be precise.
The details of the solution don't matter for the purpose of this article. What matters is that by simplifying this initially seemingly unsurmountable problem in a meaningful way, we obtain a tractable problem that we can solve with clever math.
Back to the real world
Now comes the real question: How well does the answer to the artificial, simplified, abstract problem we ended up solving apply to the real-world problem we started with?
The answer, of course, is that it depends. It depends mostly on how important the details we left out were. Every abstraction is a simplification, and thus, any solution to an abstract problem is, at best, an approximate solution to a real problem.
But approximations are all we can have in practice. All real-life problems are infinitely detailed if you take everything into consideration. If you want an exact answer to a real-life problem, you may need to consider the physics of the entire universe!
So, the answer to how good the approximation is, is this: as good as your abstraction is at capturing what is truly essential to the problem and throwing away what doesn't matter.
In this particular case, we'll have a pretty good solution, assuming the average times we assign to each road are really characteristic of said road and not just random values. But there is something even more valuable in what we've done, which is why abstraction is such a powerful tool.
Let's take a look at the bigger picture.
Why abstraction is so powerful
The power of abstraction can be seen in at least three different levels.
Abstraction as simplification
We've already seen the first level: abstraction makes problems easier to solve. Since abstracting is simplifying—as we are, by definition, removing details—we end up solving a problem that is, in some sense, smaller.
In our path-finding example, we simplified things a lot by deciding we didn't care for the width of the roads, the elevation of the terrain, the pavement quality, the weather, and many other details that would make the original problem impossible to solve.
In this simplified version of the problem, we can actually do some math and come up with a sensible solution. Perhaps, as in this case, we can even prove we have an optimal solution (to the simplified problem, that is). Thus, abstraction can make hard problems tractable or at least a bit easier.
Abstraction as generalization
But the second level is even more profound. By abstracting away the things that made our example problem related to cars, roads, and pedestrians—since, ultimately, those things weren't essential—we actually ended up solving a more general problem: finding a minimum-cost route in a graph.
In doing so, we not only solved our mundane go-to-work problem but also solved all the problems of going from anywhere to anywhere else with minimum cost, as long as we only cared about the cost of each connection. And since we didn't define cost in any particular way—just some number that must be minimized—we can apply this solution to time, money, fuel, and any other magnitude we care about!
Now, instead of a car in a city, think of a network of airports worldwide where you want to reach a destination by paying the minimum price. The abstract version of that problem is the same as this one if the cost of each route is the ticket price.
Likewise, we can think of minimizing transmission time in a computer network or even maximizing the probability of a message reaching a destination. All of these are instances of the minimum-cost path-finding problem we just solved!
Thus, since an abstraction is also a generalization, in another sense, you end up solving a bigger problem than you originally had and get almost for free the solution to a potentially infinite number of similar problems.
Abstraction as conceptualization
Finally, there is a third level, but you must trust me here because it's hard to give you a simple example. The thing is, by abstracting problems, we can sometimes find answers to questions that weren't even conceivable in the concrete problems.
This works because abstraction is also a form of concept creation: you create new concepts and relations that unlock new ways of thinking about things. These new concepts, in turn, let you discover new truths that were invisible before.
Some of the most important ideas in computability theory, such as the existence of specific, non-trivial, and relevant problems that cannot be solved at all, are only possible because we have very abstract notions for the concepts of algorithm, computer, and problem.
So, if abstraction is such a powerful tool, how can one get better at it?
Mastering abstraction
The short answer is that there is no shortcut, no easy formula. You get better at problem-solving in any domain by solving a lot of problems in that domain.
However, I can give you a concrete tip here. It's going to sound like a plug, but hear me out.
Abstraction is a very powerful tool for solving real-life problems in any domain. But there is one particular domain where it shines: computer programming.
The reason is that computer programming is, by definition, an exercise in abstraction. Digital computers are almost incapable of dealing with the fine-grained nature of real-life problems. Programming languages require very precise, clear instructions and definitions. So, any solution to any problem using a computer is a solution to an abstract problem.
If you want to improve at coming up with good abstractions, whatever you work on, you can start by learning to code. Getting any level of expertise in coding will force you to learn to abstract problems as a side effect.
In a sense, learning to code is like going to the gym, but for the mind.
Some people are professional athletes. Some even compete at the Olympic level in weightlifting. Some go to the gym for fun or just to show off. But almost anyone, regardless of their daily job, could benefit from a healthier body, stronger muscles, and more agility.
Likewise, some people code for work or fun. But almost anyone would benefit from thinking better, and being better at abstract thinking is one of the best ways to improve your general problem-solving skills.
This would be just about the right time to promote my programming book or course, but I won't. Truth is, you don't need me for that. Search online, pick the top 3 or 4 results, try them out, and choose the one that best fits your learning style and pace.
Now, don't get me wrong. I've said before most learning resources out there suck, but that is about learning to code at a professional level. Just like professional athletes need state-of-the-art equipment and world-class trainers, but if all you want is to get a bit healthier, buying a couple of dumbbells or even just doing half a dozen push-ups every day gets you most of the way there.
So there you have it. Learn to code. Doesn't matter which language or development stack. Just grab a coding tutorial and get used to doing a couple of programming exercises every week. That alone will make you a much better thinker in time.
Closing remarks
In a previous article, I described the main principles that guide my educational ethos. One of these principles is show, then tell. The basic premise is that explaining a complex idea is best achieved by first showing things and then explaining how they work.
Some of you asked me for examples of these principles. This article is one example of show, then tell. First, I showed you how abstraction works in a concrete example and then explained the general concept.
Actually, this article is a meta-example of show, then tell. First, I showed you how show-then-tell works, and now I'm telling you about it. Hope you enjoyed it ;)
Going back to the main topic, this article is part of a series I'm writing on how to bring the tools of computational thinking into your daily life. If you want to learn to think like a computer scientist, stick around. I'll be sharing a lot more in future articles.
I wrote code in the 80’s. In the 80’s we had a saying we used regularly: reduce it to a known problem.
Why am I thinking of my economics degree… “let us assume”?!?! the truth of any problem solving is we need to assume or abstract or simplify away as much complexity as possible. Then testing lets us know if we got rid of something that can’t be cut from the problem/solution considerations.
Personally, I find it a lot easier to start from a very simplified version (of reality) then add back versus starting with the complex then cutting. One of the greatest shortcomings I see is the tendency for people to get needlessly lost in complexity.