Abstraction Layers
By Richard Lander, CTO | August 30, 2025
In this blog post we will break down the essential nature of abstractions in software systems, examine good and bad examples, and finally apply this notion to the abstractions we use in software delivery.
Well-architected abstractions in software design are critical. At the end of the day, abstraction is the core function of a useful application. Any app that successfully abstracts complexity, adds value.
What is an Abstraction?
A good abstraction handles complexity for the user and makes them more productive. The ideal abstraction exposes just the inputs and outputs the user cares about and takes care of all details and implementations under the hood.
Streaming platforms like Netflix abstract the details of delivering video content to your living room. Ride sharing apps like Uber abstract the details of connecting riders with drivers to simplify ride sharing. Food delivery apps like DoorDash abstract the details of ordering food from local restaurants and getting it to your front door.
Operating systems abstract program interactions with computer hardware to expedite developing and using software. Kubernetes abstracts the details of running containers on clusters of machines to make complex software deployments more manageable.
The examples are endless, but the entire point of software systems is to abstract details, allow users to worry about the high-level concerns and get more done.
Poor Abstractions
Not all abstractions are created equal.
Opaque Abstractions
If an abstraction prevents access to underlying details when they are needed, they become an obstacle. This can occur when simplification of common use cases is implemented without any "escape hatches" to accommodate edge cases. It is also introduced on purpose in some cases. Businesses commonly implement feature gating so they can up-sell to support more advanced use cases.
Leaky Abstractions
On the other side of that coin, leaky abstractions are those that fail to manage underlying details well and encumber the user with the toil of managing fine-grained details. The added mental overhead when using apps with leaky abstractions reduces their efficiency and value. While some systems suffer this problem due to poor design, it can also happen when legacy abstractions are applied to newer use cases which break down their effectiveness.
Decaying Abstractions
A decaying abstraction is one that once served a purpose well, but that has become opaque or leaky over time. As a domain develops, becomes more complex, and accumulates more diverse use cases, a legacy abstraction can begin to break down. If it is not sufficiently extensible to accommodate developments, it will become obsolete over time. This is the situation GitOps solutions find themselves in. More on this below.
Good Abstractions
Good abstractions automate tedious toil for users, providing optimum efficiency. They manage minutia where needed but provide escape hatches when fine-grained access to underlying detail is needed.
Extensible, Modular Abstractions
Extensibility is the antidote to decay. New use cases can be accommodated. Evolving requirements can be incorporated. Modularity usually goes hand-in-hand with extensibility in that discrete functionality can be contained in a new "plugin in" component. That component can be independently maintained or swapped out, and it can provide the basis for further extension by other modules.
The Linux kernel with its device drivers and loadable kernel modules is an excellent example of this, and is why it has not decayed through decades of advancement in computer hardware.
Layered Abstractions
Layering abstractions is a great way to achieve the most desirable outcomes that survive the test of time. It can be achieved when extensibility and modularity are well supported. Primitive abstractions that provide access to fine-grained details remain available while higher-level abstractions can be layered atop to handle the primitives on behalf of the user for more specific use cases.
Kubernetes provides a clear example of this. The Pod is the primitive resource that manages a containerized workload in a cluster of machines. It is abstracted by Deployment, StatefulSet, DaemonSet and Job resources for common generalized uses. Kubernetes Operators can be layered atop these - and other native Kubernetes resources - to provide convenient management of specific applications, e.g. Prometheus Operator. This highlights the value of Kubernetes' extensibility and modularity and the magic of layering abstractions.
Abstractions in Software Delivery
How do we apply this to application platforms for optimum efficiency in delivering software? The short answer is that most of us don't today. The popular state of the art is an approach we call GitOps which is a rapidly decaying abstraction that is incredibly leaky. When "cloud native" software was emerging it provided a convenient bootstrapping mechanism. However, the software we're building is growing in sophistication constantly. Requirements accumulate, user demand for capabilities are insatiable. The complexity of software design increases but our delivery systems have stagnated.
GitOps vs Application Orchestration
GitOps is essentially a toolchain assembled into a pipeline. Tools like Terraform and Helm gained popularity because they serve simple use cases well. For simple application architectures and small organizations, they can work fine. However, as applications move to production, start generating revenue, and become more involved, the GitOps method of delivery often breaks down. GitOps systems turn into Rube Goldberg machines that demand users manage a multitude of values in a maze of configuration text files. Furthermore, the poor interoperability of the various tools make deploy-time values (the configurations that are derived when systems are spun up) a juggling act to manage. Developers get bogged down and companies employ large DevOps teams to wrangle the Rube Goldberg machines.
GitOps was a useful workaround. But it's time to move forward. It's time for application orchestration.
Application orchestration takes a different approach. Instead of a disparate toolchain, app orchestration uses an integrated control plane. Instead of a pipeline, app orchestration is a distributed software system with an API and a database. Instead of config management tools, you have level-triggered controllers. Instead of leaky abstractions where users must manage thousands of lines of various configuration text files, app orchestration provides layered abstractions that expose just the high level concerns.
App orchestration abstracts workloads and runtime environments. It provides primitive resources for these concerns and provides an essential SDK to extend the system with appropriate layers of abstraction as needed. No matter how sophisticated your software systems become, software delivery remains simple and reliable.
This table provides a summary comparison:
GitOps | Application Orchestration | |
System Design | Edge Triggered Pipeline | Distributed Software System |
Source of Truth | Text Files in Git (desired state only) | Relational Database (desired and existing states) |
Implementation of Logic | Templates, Config Languages | Any Programming Language (preferably Go) |
Component Interoperability | Poor (independent CLI tools) | High (fully integrated control plane) |
Testing & Reliability | Run Pipeline, Cross Fingers | Unit, Integration and E2E Tests |
Skills Required | Speciality Languages & Tools | General Purpose Programming |
User Toil | High | Low |
The holy grail of a generalized self-service developer platform has never been widely available for complex production use cases. However, Threeport, an implementation of the app orchestration approach, offers exactly that.
If you need a multi-cloud platform that supports any type of runtime environment and every tier of deployment, get in touch. We're confident you'll find Threeport fits the bill.