Skip to main content
Post Made Community Wiki by Fuhrmanator
Source Link
tdammers
  • 53k
  • 15
  • 112
  • 155

When people say that abstractions hide implementation details, they don't actually mean "hide" in the sense to make it hard to find. What they mean is separate implementation details from public interface, to keep the interface simple, concise, and manageable. Just like a car "hides" most of its vital parts, and only offers a fairly rudimentary set of controls to operate them, a software module "hides" most of its functionality deep in its bowels and only exposes a limited number of access methods to drive it. Imagine a car where you had to manually operate all the engine's internals (and there's a whole freaking lot of them), you'd have a really hard time keeping an eye on the traffic and finding the way.

But keeping the interface simple is not merely an aesthetic thing; it can make the difference between a successful project and a Death March. Let's play devil's advocate for a minute; imagine a software project without any abstractions at all. If you need to keep a value around, you use a global variable. If you need to use functionality more than once, you copy-paste it. If you need two different versions of a certain code section, you copy-paste, wrap it in an if statement, and modify both branches. Technically speaking, it works, but a few months down the road, you'll be fighting a few really nasty problems:

  • When you find and fix a bug, it is likely to also exist in other copy-pasted instances of similar code, so on top of finding and fixing the bug, you also have to go hunting for other occurrences and fix them, too.
  • In order to find a bug or implement a change, a maintenance programmer must be able to understand the relevant code. The difficulty in doing this increases with the size of the relevant code section, but even more with its scope. Keeping half a dozen variables in your head while mentally stepping through some code is doable; but if you have a few hundred of them, your productivity is severely impacted (I like to compare the thought process with a program that runs out of physical RAM and has to dip into the swapfile: instead of reading through the code fluently in one go, the programmer has to jump back and forth to look things up).
  • The scope of a piece of code also impacts the size of the codebase one has to dig through in order to find the bug. If you have a ten-line function with two parameters, and no globals, and you know the values of the input and the line at which it crashes, finding the bug is usually trivial and often requires nothing more than looking at the code. If it's a few hundred lines, twenty parameters, fifteen globals, and calls a few other functions of similar nature, you're in for some serious pain.
  • Without proper abstraction, any change can potentially impact large parts of the codebase, as practically anything may depend on the code to be changed. A typical symptom with such codebases is that you make a small, seemingly innocent change, and a completely unrelated feature suddenly breaks. With abstraction, you can limit the amount of damage a change can do, and you make the impact more predictable. If you change the name of a private field, you only have one source file to check; if you change the name of a global variable, you need to run through the entire codebase.

In a badly-abstracted codebase, the impact typically grows exponentially with the size of the codebase, that is, adding a constant amount of code increases the maintenance effort by a constant factor. To make matters worse, adding more programmers to a project does not increase productivity linearly, but logarithmically at best (because the larger your team, the more overhead is required for communication).