Code Parsimony and the Perils of Over Abstraction

“Make things as simple as possible, but not simpler.” – Common (but good) misquote of Einstein.

The more I code, and especially the more I work on other peoples code, the more I think that simplicity is one of the most critical aspects of quality software. By simplicity I mean conceptual simplicity. That the code is easy for someone relatively unfamiliar with it to really understand the code. To grok it.

I think there are three main obstacles to writing simple code, they are:

  • Unnecessary (premature) optimizations
  • Not refactoring/cleaning up your code once something changes
  • Over abstraction

I would expect that most programmers know to avoid the first two. The third is more insidious. 

Usually programmers are taught (correctly) that abstraction is a good thing. Often the solution to a hard problem is finding an abstraction that makes the otherwise hard problem trivial. There are other numerous well known advantages to abstracting your code and  making your code (usually) easier to understand is one of them. But at a certain point further abstraction makes your code harder to understand, less simple.

An easy example to pick on are overuses of C macros. While C macros provide a powerful (if error prone) mechanism for abstraction, they almost always make code harder to understand. You might be tempted to use them to make snippets of code more generic and reusable but unless you are going to need to reuse these snippets often, it’s probably not worth it.

Over abstraction is probably the code-sin that I’m most guilty of. I love making abstractions that make my code smaller, more generic with fewer duplications/redundancies. However I have a tendency to take this too far (though not with C macros, ewww). I over abstract. I make something generic that I really only use in one place. I restructure algorithms awkwardly so that they can share some common steps. This makes my code unnecessarily complex.

Fortunately, I think I’m kicking this bad habit. I’ve learned that you shouldn’t write code at the limits of your abilities, pushing abstractions to their limits. Code is almost always easier write than it is to read or debug. If you’re writing code at the limit of your skill, chances are no one (including you!) will be able to understand or fix it later when it breaks. And bringing new people up to speed on your project will be very difficult.

So the next time you’re having a blast abstracting the living daylights out of your code, pause for a second and consider if your abstraction actually makes your code easier to understand. If it doesn’t, do you really need it? Are you sure there isn’t a better way?
All things being equal, simplicity outweighs abstraction.