Software Principle 7: Under abstraction is almost always better than over abstraction.
Abstraction is a powerful tool. Some of the greatest inventions in technology: operating systems, programming languages, compilers, etc. are abstractions. However, when misused or misunderstood, they can be destructive.
Over abstraction can create unmaintainable, untestable monstrosities. Under abstraction may mean duplicate code, but duplicate code is almost always better than unmaintainable code.
That is until it becomes unmaintainable itself.
Duplicate code can be fixed via factoring; the over-abstracted code may not be fixable. This leads to the question: if under abstraction is still bad but still better than over abstraction what is the right amount?
How Much Abstraction is Necessary?
The answer depends on your context, like almost everything else. The context as it pertains to abstraction has three primary components: domain, team, and mission.
Context is so important to abstraction because some domains require abstraction, but the team might not be able to manage several deep abstractions.
This scenario is common in complex domains like the medical field and startups that hire engineers right out of college with little to no real-world experience.
The mission our system will be enabling is important because it exists on a spectrum. Some missions span only a month while others span ten or twenty years.
Since architecture is business and therefore mission enablement, abstractions can ensure that the mission that we’re on is achieved.
The Impact of Domain
Let’s start by focusing on the impact of domain on abstractions. Each domain that a company or team executes in has varying amounts of complexity contained within it.
Domain in this context refers to any unit of work that requires subject matter expertise. This means it can refer to actual business domains like the legal or medical domains, or something like a file format like PDFs.
Abstraction helps encapsulate and contain that complexity into components allowing individuals to black-box it as they think about that complexity.
If you are working on PDFs but don’t understand the file format or how a PDF is structured, you can install an open sourced library that shows how they are structured and learn how to work with that particular file format. These abstractions are great for team efficiency but aren’t free.
If something goes beyond the scope of the libraries that exist, but is required to solve a problem, then the team must learn the part of the domain that is required. In some cases, this may be the entire domain, which greatly reduces their ability to get value created in the short term.
Role of Team Dynamics
Teams have varying levels of talent, experience, skillsets and size. As this pertains to abstractions, some problems require abstractions that are beyond a team’s ability.
If the problem domain isn’t fully or mostly understood by the team executing on it, then abstractions will be missed or placed at too low of a level to be truly effective.
It’s important to ensure that a problem is analyzed and complexity, pitfalls and the problem domain is understood well enough to ensure a team capable is selected to do the work.
This is one of the reasons there are usually enterprise architects at large companies to ensure work is assigned to the right teams. If a team or person is under-equipped to think through the right abstractions, pair that person/team with one that does.
Nonetheless, even for a “perfect team”, landing on a great abstraction the first time through is demanding. Making too many assumptions up front if a domain isn’t understood will result in a leaky abstraction or worse: the wrong one.
Building an abstraction pieces at a time is often the best way to land on one that is perfect for the problem being solved. Preparing your systems to iterate on the abstractions that it contains therefore is incredibly important.
Length of Abstractions
Abstractions can last months or years. If your company is on a mission and will be around for ten years, abstractions that last will assist in growth. Conversely, if you are trying to build a prototype that will be around a month, abstractions don’t matter as much.
Picking the proper abstraction for the job means knowing the context of the mission that you’re working on and how long that abstraction will be useful.
Abstractions that were built incorrectly and used for too long become leaky and distracting as they don’t quite solve the problem entirely anymore. Building the abstraction in a way that allows it to be refactored later is how this can be avoided and to ensure that eventually the best abstraction is chosen.
There are great design patterns that allow abstractions to be quickly refactored, I recommend reading up on both functional and object-oriented design patterns.
It is also important to maintain a log of abstractions that are created and the logic behind the creation so they can be revisited for refactoring when a problem occurs. This will provide insight into previous decision making and will help with new abstractions.
Abstractions are essential for efficiency and effectiveness of a developer and teams. Right-sizing decisions and ensuring that abstractions are constantly revisited is vital for system growth.