There’s a lot of big and scary technical mumbo jumbo in the world of Software Development, which causes no end of pain to both the beginner and the experienced. One of these technical terms that you might hear a lot is ‘Abstraction’ — easily one of the most confusing terms in the field.
So what exactly is abstraction? As an adjective, abstraction means ‘existing in thought or as an idea but not having a physical or concrete existence’, and as a verb, it means ‘consider theoretically or separately from something else’. These two quite literal definitions put together are precisely what an abstraction is.
An abstraction is a theoretical idea. Great, more big words, but what does it actually mean. In programming, an abstraction is really just a way to simplify a complicated idea or action. Let’s look at what is easily the most common abstraction that every programmer knows are uses.
What’s the first thing you learn as a beginner programmer? The classic Hello World program where you print some text to the console. But have you ever stopped and wondered how this actually works under the hood?
The standard Hello World in most languages usually looks something like this:
The majority of programmers don’t know how this single line of code actually works under the hood. And they don’t have to either. All you need to know is what inputs to give the print() function in order to make it work.
That’s all an abstraction is. It taking something that is complex and reduces it to a simple matter of inputs and outputs. Sound familiar? It should because that’s exactly what every function is, an action that you give some inputs to, expecting some output.
An abstraction is a way to manage complexity. It’s taking something that is inherently complex and making it simple to use and work with. Printing something to console is actually a fairly complex task under the hood, but we as developers don’t have to worry about that. For us, the process of outputting text to the console is abstracted away from us.
Functions aren’t the only kinds of abstractions, though they are the most common and easiest to create. REST API endpoints are abstractions: you don’t know where or how they get their data, you simply make a request, and expect a response back. Database management systems (DBMS) are abstractions: you don’t know how or where the database is storing data, only how to interact with it and query it. Libraries are simply collections of functions, and so are just a collection of useful abstractions.
As programmers, we reach for abstractions all the time without even realizing it. Now that you know what an abstraction is, the next step is to start creating abstractions of our own. An important part of abstractions is that they are always specific to the system that you are working in.
Let’s say that we’re given the task of implementing the core persistence layer of our architecture. A big task for sure, our main goal being to make data persistence easy and painless. The easiest way would be to interact with whatever library we’re using to interact with the DBMS. But this creates possible future tech debt. What if our current database solution turns out to be inefficient, and we need to change part of our tech stack later?
There are several ways to solve this problem. For our purposes, we’ll look at a service-based approach. What if instead of interacting directly with our database (or database library wrapper), we create a ‘Persistence’ service. This service will be in charge of taking in data, and a unique identifier, and persisting that data under that identifier.
It might look something like this PersistenceService.saveToId(id, data) How does this benefit us? It means that we are no longer creating a hard dependency on our database, which means that if we wanted to change whatever database solution we’re using in the future, we no longer have the refactor every line of coding relating to hard persistence. We only have the refactor the internals of the persistence service itself.
That’s the true benefit of abstractions. As a programmer, abstractions mean that we don’t need to fully understand every single implementation of our system. We just need to know what inputs to give an abstraction to get the outputs that we actually want.
Something that’s also important to note is that we haven't talked at all about any specific technology or programming language. Abstractions are universal to every programming language and exist in every single codebase. Unless you’re writing your program in binary, you will never be able to escape abstractions as a programmer.
Today, modern programming is built upon layers and layers of abstractions. We use these abstractions to create powerful systems of managed complexity. Because at the end of the day, that’s what abstractions do for us as programmers. Abstractions are the keys to managing and controlling complexity.