Simplicity is Complicated

The favorite rhetorical fallback of politicians of every stripe is “it’s for the children”. Anything can be justified in terms of making things better for children if you frame it right. Lately I’ve begun to think that the word “simple” is the programmer’s version of “for the children”. We use it to justify all manner of decisions.

I’m not excepting myself, either. I see myself doing this all the time. “It’ll make things simple”, I say. Which by a strict interpretation is true. Whatever direction I’m advocating, it usually makes [some] things simple. It usually also makes other things more complicated.

Let’s look at an example of two opposing kinds of simplicity. My old boss Chris Strom recently wrote about some common Ruby newbie mistakes (it’s a great article, you should read the whole thing). Here is an example he gave of code written by someone new to the Ruby language:

After several iterations of successively introducing Ruby idioms and features, the code looked like this:

Which example is simpler? Well, that depends on what you mean by “simple”. As Chris points out, the latter example is much closer to the domain. The line sum = neatly expresses the intent of the code without getting bogged down too much in implementation. It’s also more concise.

On the other hand, the first example uses language constructs that are familiar to almost all programmers, not just Ruby programmers. It is simpler in the sense that even someone who has only basic programming skills could probably work their way through it, without needing to understand concepts like #inject and symbol-to-proc.

What do we mean when we say the word “simple”? I’ve realised that we programmers use “simple” to mean a lot of different things. Some of the ways we use the word “simple” include:

Minimizing unnecessary effort. Characterised by the classic admonition to “Keep It Simple, Stupid”. Or as the XP folks say, “do the simplest thing that could possibly work“. Of course, sometimes when you choose to do less you force someone else to do more. And sometimes that someone else is you, two weeks down the road.

Hiding complexity. Ruby on Rails made web development simple, relative to tools that came before it. It accomplished this feat by embracing “convention over configuration”. In order to do this, it had to incorporate a great deal of extra complexity in the form of algorithms which guess the intent of the programmer instead of forcing her to specify her wishes explicitly. Anyone using it eventually has to become familiar with these hidden rules in order to understand why Rails behaves the way it does.

Avoiding difficult-to-understand features. In the name of simplicity, some programmers advocate avoiding “magic” language features they see as complicated and difficult, such as generics, recursion, or metaprogramming. Other coders say there is no such thing as magic if you have an adequate understanding of the language, and that arbitrarily naming certain features “magical” is superstitious nonsense.

Avoiding formal architecture. There are programmers who feel that complexity is the inevitable result of spending too much time thinking about design, and that simple code is code that does it’s job without being fastidiously decoupled and thoughtfully abstracted. Others say that this kind of thinking leads to an unmaintainable “big ball of mud“, the antithesis of simplicity.

Elegance of design. Some insist that elegant orthoganality is what defines simplicity. Simple components which do one thing well, which have few interdependencies and can be composed into various configurations. Others retort that all abstractions are leaky and that trying to abstract the leaks away only complicates the job of solving real problems.

Staying close to the domain. To some, simplicity is ability to write code which looks like a plain-English explanation of the problem. But the underlying scaffolding which makes the code so understandable to a domain expert may be baroque and brittle in it’s implementation.

Clearly, simplicity is not a simple subject.

I think the reason for this is the nature of complexity. Complexity is like an air pocket trapped under a layer of airtight plastic. If you push it down it just pops up somewhere else. Most of our attempts to “simplify” software really amount to pushing the complexity somewhere (or some-when) else. And our disagreements about what is meant by simplicity are really about where we think the complexity belongs. As a result, the goal of simplicity can be used to justify just about any course of action.

I challenge myself and anyone who reads this, next time you use the word “simple”, to stop and think about what you mean by it. As an exercise, try to rephrase it in terms of where you are pushing the complexity. Let me know if you gain any insight from looking at it in this way.