Hidden Wonders

Programming·Society·Technology

How Much Abstraction Is Too Much?


Published:

Lastmod:

Table of Contents

Introduction[#]


An idea is called abstract if the idea is theoretical or far-removed from the physical world. As a result, abstract ideas also tend to be very difficult to understand and explain.

Computers and modern technology especially involves many complex, abstract ideas; as a result, a core technique used in the development of these increasingly advanced technologies is called abstraction——simply stated, it is the concept of removing or details. Abstraction is also common in mathematics, as this simple example illustrates:

5×10 = 50`

5+5+5+5+5+5+5+5+5+5 = 50

When a student first learns to multiply in elementary school, they learn that these two expressions are equal, which is a more simplified version of this fact:

a×n = a₁ + a₂ + ... + aₙ

This is an example of abstraction: rather than write out a bunch of plus signs, mathematicians invented multiplication. All of the extra details (those plus signs) are abstracted into a far shorter term (the multiplication sign and a number denoting how many plus signs there are). Math does this a lot, with slightly more advanced examples being summations and products.

Abstraction is so useful in technical fields——particularly in computer science——that the following quote was coined:

All problems in computer science can be solved by another level of indirection.

— Butler Lampson

This adage is true, but could infinite levels of indirection eventually lead to new, more serious problems than the ones we were trying to solve in the first place?

This article seeks to tackle the concept of abstraction in the field of computer science and technology as a whole. Is there such a thing as too much abstraction? What are the problems solved with and issues caused by abstraction? All of these questions and more I seek address and discuss in this article.

Abstraction in Consumer Computing[#]


When computers were first invented, users would insert punched cards into the computer to run programs. A hundred years after this early model of the computer, programs were run by typing on a command prompt——a well known example is MS-DOS, the predecessor to Windows. Then came GUIs, which allow computer users to accomplish their tasks without ever seeing a command prompt. This made computers easier to use and more accessible, but it came at the steep price of computer users knowing less about how their devices actually worked.

Most computer users who grew up in this era still know a lot about computers and are good troubleshooters. I’d consider the tail end of this era to have been Windows Vista times, when computers were still the primary computing device for end users before being overtaken by smartphones.

Following GUIs came our current stage of computing, which I’ll refer to as the Web 2.0 era. This era still involved a GUI, of course, but now the programs people interacted with were no longer located on their own computers. The result of this? Unbelievably high internet adoption rates and the average computer user not even knowing how file systems work. The computing device of the 21st century is the smartphone, a device which gives the user limited control of their device. A file manager did not exist in iOS until a few years ago, and it’s current implementation is limited. While more complex control is allowed on Android devices, Google seems devoted to limiting user control over time. Besides, the percentage of Android users with the knowledge to flash custom ROMs or even download an APK off the Internet is microscopic in size compared to the average Google Play store using Android user.

Think about common internet slang: stuff like /s to indicate sarcasm (which I think is pretty stupid to use but it’s a good example) derives from HTML tags, where to close a tag like <p> you’d use </p>. Hopefully, readers of this article know this already (if not, try pressing F12).

This demonstrates a point: Internet users used to know about HTML and how it worked enough that we could make jokes about it. Nowadays, I try to explain basic HTML to my friend and she gets a headache. Now, imagine your average, phone-using Internet user, trying to understand HTML, and how their phone works. They would have not the slightest idea. The reason why is because of this rampant abstraction, which not only removes users from using their devices in a manner consummate with how they actually function, but also makes it more difficult to understand how the device works regardless due to all the levels of abstraction that are occurring under the hood.

Most average phone users don’t even seem to know how to work tabs in their web browsers.

Update (Jan 29, 2023): Hate to link to Reddit, but the comments in this post describe part of the problem I’m trying to touch upon in this article article. Take a look yourself, but here’s my favorite quote from the comments, which perfectly describes the problem:

Reality is hidden behind a glossy metaphor and nobody tries to look behind the curtain.

— /u/brothersand

And They Don’t “Need” To Know[#]


Why did old computer users learn more about how computer worked than users nowadays? It’s because of the increase in accessibility of computing technology. The smartphone and it’s “Apps” are infinitely easier to use than a desktop PC and it’s “programs.” They’re easier to install, easier to use, and more convenient for the everyday person to use in life. When everything just works, people use their technology without worrying about how it works——it’s only when something goes wrong when a more technically savvy user will learn enough about technology to resolve their issue.

Is this really a bad thing? No, I’d say it’s a good thing that technology has, at least in this sense, gotten better. I like it when my software works the first time around, and most people tend to agree.

However, it’s also important to understand what this ease in usability has taken away from people. The percentage of people who understand the foundations that our world is built upon is going to decrease as more and more children are born into a smartphone-first world. There will remain experts in the field, but the average guy down the block who knows how to troubleshoot problems with technology will disappear.

Abstractions in Programming[#]


The ways in which we build programs are designed with abstraction in mind. Take, for example, this simple C program:

#include <stdio.h>

int main(int argc, char *argv[]) {
	/* check args */
	if(argc > 1) {
		fprintf(stderr, "Too many arguments.\n");
		return 1;
	}
	
	/* print text */
	printf("Now we'll print");
	printf(" Lots of text");
	printf("\n\n\n");
	
	/* math stuff */
	int x = 0;
	printf("x=%d\n", x);
	x += 5;
	printf("Now we do math, and x=%d!\n", x);

	return 0;
}

Very stupid program, but what if we now wanted to (for some reason) check the argument number multiple times, or do that math to x multiple times? As the quote says, we can use some indirection to solve the issue by making some functions:

#include <stdio.h>
#include <stdlib.h>

void check_args(int argc) {
	if(argc > 1) {
		fprintf(stderr, "Too many arguments.\n");
		exit(1);
	}
}

void print_text(void) {
	printf("Now we'll print");
	printf(" Lots of text");
	printf("\n\n\n");
}

void do_math(void) {
	int x = 0;
	printf("x=%d\n", x);
	x += 5;
	printf("Now we do math, and x=%d!\n", x);
}

int main(int argc, char *argv[]) {
	check_args(argc);
	print_text();
	do_math();
	return 0;
}

Now, we can reuse our code whenever we want with functions. This is one of the simplest examples of abstraction there is when it comes to programming. Some random person can come along and look at the main function I’ve written and have a rough idea what the program is doing without needing to know the minor implementation details. This would allow us to create collections of functions for other people to use; this is called a library, and the interface through which you (the programmer) interacts with that library is called an API. This lets us easily reuse the work of others to complete our own projects quicker, “standing on the shoulders of giants,” as they say.

Indeed, abstraction is baked into all programming languages whether we like it or not in the form of standard library functions. When we call printf, no one is really thinking about how printf is often implemented in C as a wrapper function around vfprintf, and that vfprintf then uses putchar to print the characters to the screen. It’s a whole other can of worms and, if we had to worry about that every time we had to do something as basic as print to the screen, it would be difficult to impossible to write more advanced code——at the least, it’d be a lot more time consuming.

Programming Language Tower[#]


Following this logic, people have been trying to make code easier to write with newer programming languages. A notable example is Python, which is (typically) interpreted to C code. Obviously, this leads to performance hits. Proponents of these newest languages say they reduce errors——and they certainly do——but we lose something valuable.

Picture a programming language tower. At the bottom is binary (or just some electrons and logic gates if you went low enough I guess), and at the top is some crazy magically language that is built on top of a hundred other languages, that let’s you call a single magical function that will solve all of your problems. I believe that in the coming centuries of computing, this tower will continue to grow taller. Imagine now, how hard it will ever be for a single individual to understand everything going on in that programming tower. It’s a security problem, a usability problem, and, especially, it’s a maintainability problem. In a hundred years from now, will C programmers be as rare and valued as COBOL or FORTRAN programmers are now?

We need some abstraction in programming, but if we ever find ourselves too high up in the tower, there’s no way we could ever climb back down again. It becomes harder and harder to understand until there is no person capable of understanding it all, and the mess of technology becomes unsustainable.

...today’s software has become far too complex for one person or a few to understand. Because it’s libraries built on top of other libraries, etc. By the time you get to know all of it you’d probably be dead from old age.

— Jonathan Blow, Preventing the Collapse of Civilization

OOP: A Real World Example[#]


There’s a problem at the company I work at. All the people there are old Windows programmers, who’ve been programming since before I was born. However, we’re now working on software for a POSIX-compliant operating system. So, they refer to everything in imprecise terms that aren’t really accurate, and take that inaccuracy to the names of their classes. Here’s a rough example of the kind of class you might find in our code:

class Semiphore // Yes, it's spelt wrong in the actual code
{
    private:
        pthread_mutex_t semiphore;
    public:
        Semiphore(void);
        virtual ~Semiphore(void);
        void give(void);
        void take(void);
}

You’re not seeing that wrong: this class is not a semaphore, it’s a wrapper around a POSIX mutex! Now, if we were to use this class somewhere in the code, you’d be right to be scratching your head when you call take twice and hang your program. A simple look at the implementation and you’d realize that you have to unlock the mutex first!

OOP uses encapsulation to hide the detail of functions and classes away from it’s users. However, it can be a real problem if the class is poorly designed such that it’s functionality doesn’t match its function names or class name!

The Unix Philosophy[#]


The Unix Philosophy is an idea coined by the two creators of Unix and the C programming language, Dennis Ritchie and Ken Thompson. The idea——summarized by the phrase “Do One Thing and Do It Well”——is to create a collection of simple, programs that can be used together to accomplish more complicated tasks. In Unix, this cooperation between programs was accomplished by designing all programs to take input from text stream and output text stream. I mention the Unix philosophy in this article because, in my opinion, it is the quintessential example of abstraction used well.

For the unfamiliar, in a Unix shell like bash there are simple commands like ls, which list the contents of a directory. Then there are other simple commands, like wc -l, which lists the number of lines in a text stream. So, to find out how many files are in a directory, we can use a Unix pipe to redirect (“pipe”) the input of ls into the input of wc:

ls | wc -l

This is pretty simple example, but they can get more complicated and more useful. In my list of useful Linux commands, I have the following command:

# Encodes text using base64
echo "base64 text" | base64 # outputs YmFzZTY0IHRleHQK

In the documentary UNIX - Making Computers Easier to Use, the developers at Bell Labs use the following example of creating a simple spellchecker using pipelining, which I have broken down and annotated into separate lines to explain how it works:

makewords textfile | # parses "textfile", putting each word on its own line
lowercase |          # makes all words lowercase
sort |               # sorts all words alphabetically
unique |             # removes duplicates
mismatch             # checks words against some spelling dictionary

There are several major benefits of the Unix philosophy: as seen above, one of those is that it makes it incredibly easy to reuse existing code to solve new problems. Similar to functions as I discussed in previous section, this allows a large program——in the above example, a spellchecker——to be broken up into smaller, more maintainable pieces.

The power of abstraction is not to be underestimated, but it’s dangers are still prevalent here. If we make a simple script, put it in a file to create a new script, then start calling that new script in more scripts, we are getting further and further from the actual functionality of the program. In theory, all of these new processes getting spawned could have a significant impact on performance depending on how they are used. It could also hurt one’s understanding of how the program works, and it could——almost contradicting our previous claim regarding maintainability——make bugs harder to identify if the faulty program could be any one of hundreds.

The Unix philosophy is not perfect, but it’s quite good I think. Why else would it be regularly used in some form for over fifty years?

So What’s the Answer?[#]


We’ve established the following points:

  1. Too much abstraction leads to a loss of knowledge and general inefficiency.

  2. Too little abstraction makes technology too difficult to use.

Obviously, a balance must be struck between these two extremes. I am more anti-abstraction than the average person today, but I don’t suggest that we delete GUIs and go back to command-line-only, nor that we remove high-level programming languages and write everything in binary or assembly instead.

I think that people using the Internet should be expected to know what basic HTML is and that a language like C is a better language to program with than a language like Python. I believe abstraction within programming languages should be used with care——breaking up code into a mess of small functions for no reason makes one’s code to difficult too read, yet using functions to avoid code repetition is still an excellent idea.

The line of too much abstraction is very subjective. The ultimate goal of this article is not to dictate where that line should be drawn; it is merely to get people to understand that a line needs to be drawn somewhere or we risk trapping important knowledge behind endless walls of abstraction.


Home Top


Site Licensing Site last updated: 2024-06-08