Software - It's not what you think it is - Part 2

Why broken abstractions are harmful

Uwe Friedrichsen

13 minute read

Seagull on the beach

Software - It’s not what you think it is - Part 2

In the previous post of this blog series we discussed the assembly line fallacy, the misconception that software development is the same as building a car and learned that software development is part of the design, not the construction.

In this post, we will discuss the next misconception, the broken abstraction dilemma and its consequences. This is particularly relevant for the concept of AI solutions writing software based on the input of business experts. Let us get started.

The broken abstraction dilemma

Part of the storyline in which software developers lose their jobs to AI solutions is that business experts will be able to simply state their requirements in natural language and the AI will create the required code based on those requirements.

If we look back in time, we quickly realize this is just a new iteration of decade-old wishful thinking:

  • We had it with 4GL in the 1990s.
  • We had it with MDA in the 2000s.
  • We just had it (and partially still have it) with No code and Low code in the recent years.

And there were some additional attempts between these most popular attempts. All of them basically turned out to be a flash in the pan. They never achieved any widespread market relevance except for a few niches (where they admittedly are quite useful).

From what I have observed, the business experts themselves were major drivers of the quick demise of all those approaches. Their behavior and their demands made the success of such “the business experts directly create working systems at a requirements description level” approaches impossible.

To understand the problem, we need to take a few steps back first.

3GL gives you all the options

Basically, a modern computer can implement a lot of things. There is an infinite number of things a computer can execute. If you want to have all options available that a computer can do and show, you usually use a so-called 3GL, a third-generation programming language like, e.g., Python, C, C++, Java, Javascript, C#, COBOL, or alike. All these languages can be used to basically implement everything that can be implemented using a computer. You have all degrees of freedom at hand a computer offers.

The problem with 3GLs is that even if they are a lot easier to understand and use than machine code (1GL) or assembly language (2GL), they are still not easy to use. You need a certain education. You need to understand how to translate a requirement from outside the computer world into a 3GL description that implements this requirement. Depending on the requirement this can be very tricky. That is why usually software developers do that “translation” job.

I put the word “translation” in quotes because this is not a mere translation job. Computers “think” differently than humans which means there is usually not a direct way to turn a requirement into code. Often this is a tedious job, especially if there are a lot of requirements that all interfere with each other (which is the norm for most software solutions) which is hard to get right.

You also need to think of all possible edge cases that humans do not tend to think about because they implicitly know how to deal with such a situation. But a computer just executes one command after the other. It just blindly follows rules described in the 3GL. It does not know what to do unless it is described in the code. As a consequence, it can be a lot of work to turn a seemingly simple requirement into code.

So, turning requirements into code often takes a lot of time and work. Additionally, the typically project-oriented software development process adds to making everyone wait for a long time until the seemingly simple demands become part of the corresponding software solutions. This understandingly causes impatience and frustration, sometimes even adversity regarding all this “obscure work those software developers do”. Why does it take so long? There must be a faster way! Can’t we just take those pesky software developers out of the loop and let the business experts directly create the solutions?

Abstractions to the rescue?

But letting the business experts write 3GL code is not an option as this would mean business experts need to learn how to translate their needs into something computers can understand. But maybe … if we are able to simplify the coding task? So, people considered providing programming systems at a higher abstraction level, a level that business experts also understand and can program. The basic idea of increasing the abstraction level is to “solve” basic programming tasks upfront and offer the solved task via a higher-level abstraction.

E.g., instead of manually coding a page that displays an order including its details, a higher-level abstraction “display order” is provided. And whenever you need to display an order, you simply write “display order” instead of writing the required code in 3GL. This idea could also be generalized to a general “display” command that can be applied to all types of entities, like “display customer” or “display product”. Maybe a few options can be set to choose between a few different display variants but this is basically it.

As a consequence, the developers of such systems do not need to understand the details of the underlying 3GL, or the intricacies of HTML and CSS needed to display a web page. They just write “display order”, providing the respective order as a parameter (or the abstraction also takes care of selecting the required order implicitly).

This is something a business expert will use much more likely. At least, anyone who can describe a business process should be able to develop a solution at such an abstraction level. Or not?

Well, in theory this could work (and in a few places it does). It just turned out that the business experts themselves usually pose the biggest problem – not in terms of their programming skills but in terms of their demands.

Abstractions reduce the degrees of freedom

The point is that going to a higher abstraction level means taking options away. With a 3GL, you can program everything a computer can do and display. The price is a very low abstraction level. If you want to raise the abstraction level, you basically need to choose one way to do something from the infinite possible ways of doing it.

E.g., “display order” will display orders in a certain way, like which information is shown, how it is arranged on the page, where the “Ok” button is located, how it is named, and so on. Instead of individually being able to arrange which items are shown where on the screen and how a user can possibly interact with them, the layout and interaction options are predefined.

While this is just an example, the underlying principle is a basic truth:

Raising the abstraction level always requires removing degrees of freedom.

If you want to have all options, you need to go for the lowest abstraction level possible. With computers, this is a 3GL. Whenever you want to use a higher abstraction, you need to remove degrees of freedom. You need to accept that a certain type of task is always implemented in the same way – one way of doing things, no alternatives, no tweaks. Of course, you can add some configuration options to allow variants. But in the end, the basic problem persists. You do not have all possible options at hand.

This is all fine and would not be a problem if business experts would voluntarily resort to the options available. If they would look at the abstractions that are available and accept that a certain task is limited to be represented and executed in a given way, all would be easy.

Breaking the abstraction

The problem, I have observed all over my career is that business experts never accept the limitations of a programming system based on a higher-level abstraction. They are sort of “spoiled” from all the options a 3GL programming system offers and always insist in having it “their way” and not the tool’s way 1. As I commented in the associated footnote, I do not mean “spoiled” in a negative way. Instead, we face a tradeoff between conflicting forces here.

Still, based on my experience, the requirements almost always end up the following way: “We want XYZ as the tool provides it except for the following N places where we need to have it a different way.”

This means breaking the abstraction. You want the system to do something, it cannot do if you stick to the abstraction.

The first consequence of breaking the abstraction is that business experts will not use the programming system but leave it to software developers because they want stuff the programming system does not deliver out of the box.

For a developer, breaking the abstraction means you are in a place you do not want to be. Either you need to understand the internals of the programming system to find a way to tweak it.

Or you need to modify the often generated code after its generation to implement the desired behavior, including finding a way to preserve your changed code over code generation cycles. This basically means programming against the abstraction which is notoriously hard.

Typically, you also lose the ability to upgrade the programming system because usually they do not provide any guarantees, the next version of their system will generate code in the same way. In the end, the whole point of a higher-level abstraction is that you should not need to know anything about its internals. Thus, changing the way, the abstraction is implemented internally should always be possible as long as you stick to the abstraction.

Or you need to use an extension mechanism if the programming system provides one. However, doing that usually means opening a different kind of Pandora’s box. First of all, this means the business experts will most likely start to ignore the limitations of the abstraction and act as if you were developing the system using a 3GL but still expect the higher development speed of the higher-level abstraction 2. And second, if you overdo it, you again lose the ability to upgrade your system to a new release version.

The most prominent example of these kinds of open-for-extension systems is SAP. The original intention of SAP was to harmonize the way how things are done. It ended in the opposite. Often the license costs for the software are a single-percent fraction of the costs required for “customizations”, i.e., for breaking the abstractions SAP provides out of the box.

And quite often, my clients told me they were not able to upgrade their SAP installations to a new release version because they overdid the customizations. As a consequence, sometimes the clients lagged more than 15 years behind the current SAP release and it cost them a high fraction of their IT budgets – often over several years – to upgrade to a new release because they need to go through millions of lines of customization code and make sure it will work with the desired target release before they actually can do the release upgrade.

Or the programming system already offers to work at several different abstraction levels out of the box but forces you to use the design and implementation tooling designed for the highest abstraction level. E.g., the tooling is based on diagrams and (simplified) expects you to draw boxes and connect them with arrows. Breaking the higher-level abstraction then means to create huge diagrams with tons of lower-level abstraction details which is extremely tedious and error-prone to do.

Additionally, nobody understands the increasingly complex diagrams anymore after a short while. And as these programming systems never were designed for such an amount of details, they also do not provide the support to navigate in a complex “code base” like a contemporary IDE does for regular 3GL code.

We have seen that a lot with 4GL tools and even more impressively with MDA. Even simple tasks quickly ended up in huge poster walls as soon as soon as you did not stick to the highest abstraction level.

And so on. There are a lot of other variants around but all of them have the same issues. As soon as we break the abstraction, business experts are no longer able (and willing) to “develop” on their own, development becomes a mess, integration also tends to become a mess, maintainability becomes even messier and the ability to upgrade to a new version of the underlying programming system often is lost.

Additionally, as soon as you break a high-level abstraction, you tend to fall back to an abstraction level where you basically need to put every implementation detail into the “design” to get the code right. It is not just a nice, clear diagram or a few sentences of structured text. It quickly becomes poster walls of diagrams or books of text.

Second interlude

In this post, we discussed the broken abstraction dilemma. We have seen that if we want to be able to describe our demands in a short and concise way, we need some kind of implicit or explicit high-level abstraction that enables us to specify things in a concise way.

As soon as we break the abstraction, we not only work against the abstraction to get the deviating demand implemented. Usually, it requires one or two orders of magnitude more specification details and results in brittle solutions that are hard to maintain and change.

We also saw that most of the time, we are either confronted with incomplete abstractions or requirements not accepting the limits of the abstractions, both leading to the aforementioned situation that we need to bypass the existing abstraction and implement parts of the solution at a lower abstraction level – with all its consequences.

As this post already has become quite long, we will discuss what the broken abstraction dilemma means for AI solutions in the next post of this series before then moving on to the greenfield fallacy. Stay tuned …

  1. I mean “spoiled” in a friendly way. Basically, the business experts are used to the virtually unlimited flexibility of 3GL software development and thus never needed to learn to fit their needs into a limited representation and execution system. If a given representation was not adequate, a new one was created that fitted their needs exactly. Of course, we face a tradeoff here. On the one hand, business needs to be able to work as smoothly as possible which pushes in the direction of doing everything exactly the way they are used to doing things. On the other hand, this often leads to overly complicated solutions that are overly expensive to create and to maintain, and a few minor changes in terms of streamlining things would have saved an enormous amount of time and money in IT. These two forces are not easy to balance and I doubt, there is a “right” way of doing it. Always implementing every single quirk a business expert demands without critically questioning it, hurts companies more than it aids them. Standardizing everything on the other hand will impede the business which also hurts companies. Hence, the “truth” as so often lies somewhere between the two extremes. ↩︎

  2. Again, the business experts are not bad persons and they do not act malevolently. It is just that they try to optimize for their needs and pains. And if virtually everything is possible at the IT side, they will aim for a solution that fits their needs best. And as they are not IT experts, they usually do not realize the consequences of their demands on the solution side. To be fair: It is not their job to understand the consequences of their demands on the solution side. It is our job as IT experts to help them understand such consequences and show them alternative options. Still, based on my experience it will always be a difficult discussion. ↩︎