Abstraction is one of the most important concepts underlying modern technology today. With it, we are able to build more impressive, more complex, and yet even more intuitive hardware and software. Still, despite it’s incredible influence, it’s a surprisingly little known concept.1
I aim to change that with today’s post.
When Life Gives you Lemons
Let’s start by laying some groundwork:
I like to define abstraction as a reduction of complexity, accomplished via a separation of concerns.
To explore the concept, let’s say that you’re running a lemonade stand and squeezing all your lemons by hand. Hard work, right?
One day, a mysterious figure brings you a massive black box with a tube on one side and a nozzle on the other. He tells you that if you place whole lemons into the tube, lemon juice will come out the nozzle on the other side. You give it a try, and it works!
At this point, you ask, “But how does it work?”. He smiles suspiciously and replies, “Does it matter… as long as it works?” before vanishing in a puff of smoke.
Try as you might, you can’t open the black box, so you start theorizing…
- Maybe the black box contains a mechanical lemon-squeezing machine, or
- maybe it just contains another human who is still squeezing the lemons by hand, or
- maybe it contains a newly discovered animal which eats whole lemons and urinates pure, delicious lemon juice.
As your lemonade sales start skyrocketing, you begin to agree that it doesn’t matter how your black box works, as long as it works. So, over time you stop worrying about how the juice is generated and come to rely on “lemon input” equaling “juice output”. Congratulations – you’re now free to focus on other aspects of running your growing lemonade enterprise!2
This separation of concerns was accomplished via a theoretical boundary called the “abstraction layer“, whose abstraction is often literally referred to as a “black box“. This is because the only thing that really matters is what you put in and what you get out, so the inner workings might as well be contained within a black box.3
And that’s abstraction in an (opaque) nutshell.4
Driving Toward a Point
OK – but how realistic is the example of a magic lemon-squeezing black box? I see your point, dear reader, and I raise you one leading question:
“Do you have a driver’s license?”
Actually, it doesn’t matter. A girlfriend back in college never learned to drive,5 and I’m sure even she would be familiar with our next example – a concept which hasn’t changed for the last 120 years.
If you’ve ever stepped into the driver’s seat of an unfamiliar car, you might have been flummoxed by any number of features, lights, and toggles. But in a world of confusion-inducing manual transmissions, “engine-start” buttons, and self-driving AIs, you can always count on the good ol’ steering wheel.
After all – using one is dead simple:
- Turn clockwise, and the vehicle turns to the right.
- Turn counter-clockwise, and the vehicle turns to the left.
In this way, the simplicity of a steering wheel forms an incredible abstraction layer to the complexity of a car’s steering system.6
This is very similar to our lemon squeezing example from earlier – we focused on the simple input and output (lemons and juice, respectively), while the complexity of what’s inside the black box was hidden from us.
So what hidden complexities does a steering wheel hide? Here are a few examples:
- Wheels move up and down based on the car suspension, but the angle of the steering wheel never changes.
- When cornering, the inner front wheel must turn sharper, since it travels less than the outer front wheel.
- It takes a lot of force to turn a heavy car, but a 15-inch diameter steering wheel enables a driver to exert 16 times less force than it’d take to swivel the road wheels directly.
I’ll bet you’ve never concerned yourself with any of those things, thanks to the abstraction of the steering system. As a result, anyone can get into a friend’s car and the last thing they have to worry about is knowing how to steer the thing.
There’s also a number of general advantages that good abstraction enables:
- Easier Troubleshooting – If an issue occurs, abstraction makes it easier to identify what part of a system might be at fault.
- e.g., For a car to turn, two things must happen: 1) a driver must properly turn the wheel, and 2) the steering system must turn the wheels. It’s possible for one, the other, or both to malfunction.
- Dependency Reduction – Parts of an abstracted system are self-contained, which minimizes their dependence on other parts of the system.
- e.g., A steering system doesn’t rely on any specific driver to work properly, and likewise, any driver can technically steer any car.
- Interchangeable Parts – Parts of an abstracted system may be interchanged with any other parts that maintain the same interfaces.
- e.g., There are two primary types of steering systems – rack-and-pinion and recirculating-ball – but both are exposed using the same steering wheel interface.
- Translatable Interfaces – Users can learn an interface once and translate their knowledge to all interfaces which follow the same rules of abstraction.
- e.g., If you know how to steer one car, you know how to steer any car.
- Compatible Iteration – Modifications to either side of an abstraction can be made without losing compatibility with the other side, provided the interfaces are maintained.
- e.g., No drivers had to learn anything new to use power steering, because it didn’t require any changes to the design of the interface (i.e., steering wheel).
A Real Business Application
At Plastiq, we use abstraction all the time to build more modular, scalable, and flexible systems. One particularly salient example for us is fee calculation.
Plastiq is a service which enables credit and debit cardholders to send payments to any business in North America, regardless of whether the business accepts cards. Our customers enter their card information at plastiq.com and tell us who they want to pay, either by selecting one of the 20,000 billers already listed in our system, or by specifying a physical address where we can send a check. Then, we charge their cards for their payment amount and forward the funds onto the business they entered. We assess an additional fee for processing each payment.
Initially, we had “hard-coded” the fee calculation rules into our card processing system. This looked something like this:
- If a MasterCard is used, charge X%.
- If a Visa is used, charge Y%.
- If an American Express is used, charge Z%.
In other words, the system was designed to determine a percentage-based fee based on the single variable of which card brand was supplied.
Over time, however, we realized that we could take advantage of special cost discounts we were qualifying for based on other variables (e.g., where a card was issued), and pass on the savings to our customers in the form of lower fees.
Now, we knew that we’d probably identify other areas for fee optimization in the future, but we didn’t yet know what variables they would depend on. Rather than continue to update our payment processing flow each time we wanted to change fees, we abstracted our fee calculation into a completely separate system which takes in the entire context of a payment (i.e., everything we know about it – all the variables), and returns a single number indicating the fee calculated.
By doing so, we’re able to create and update rules very quickly and easily, and push the changes live without worrying that doing so could break our payment processing flow. Furthermore, we can take advantage of all the benefits of abstraction outlined above.
The Final Word
Abstraction is a very powerful concept, but its value is highly dependent upon how it is built into a system. I’ve seen and designed both great and horrible abstractions, and the best way to understand its advantages and pitfalls is to try to design some yourself.
If you’re interested in practicing it yourself, try the following example:
- Design a water fountain. Go ahead and take out a pen and paper to do so. Be sure to answer how a user can start and stop the flow of water, and what types of experiences are supported (e.g., drinking straight from the tap, filling a water bottle, etc.). Also, how does the water fountain hook up to a water source?
- Now, change that water fountain to enable both water and Hawaiian Punch. Up to you whether or not a user should be able to get both at the same time. Consider how and where the Hawaiian Punch comes from (Is it stored in a reservoir? Is it mixed from concentrate?).
- Now, change that last design to enable up to six arbitrary drinks which may be easily swapped in and out. Maybe some drinks are carbonated, and maybe others have special ingredients like the bubbles in bubble tea. How do you deal with the possible introduction of these other variables?
Congratulations, you’ve kind of just developed a soda fountain!
Do you think you would have designed the original water fountain differently if you knew we were eventually making a soda fountain? This is the difficulty many companies encounter as they expand their business models – lots of businesses can’t know that they’ll end up making soda fountains (metaphorically speaking).
Now, read up on how soda fountains actually work.
You’ll notice that a number of components are abstracted quite well in the design of a standard soda fountain. In particular, the drinks are all broken down into their primary components: CO2, flavoring, water, and ice. Then, the machine is equipped to vary the proportions and mixtures of those four to produce each drink. In that way, if you want to replace a type of drink from the machine, you can just change up the flavorings and program different proportions of the main ingredients, while using the same machine. Pretty cool, huh?
Alright – go get ’em, tigers.
- At least outside of computer science circles.
- Like putting those pesky O’Doyles out of business.
- As an aside, in 2006, a German job-hunting service called Jobsintown.de ran an awesome ad campaign, which explores this “black box” notion for machines whose operations we often take for granted.
- Abstracted systems are often described as “opaque” because you don’t have to (and often can’t) know how they work on the inside.
- Something, something, home-schooled in New York.
- An abstraction layer is also called an “interface“, because it defines how other systems and users might interact with the abstracted system.