Friday, February 17, 2012

Convention Over Complexity

Some Thoughts on the Virtue of Simplicity

There are good ideas that you can recognize as good ideas, yet not really appreciate just how good they are until you live them. Two such ideas is are simplicity and convention. I first encountered the notion that these could be a powerful pair in the excellent book How Buildings Learn by Stewart Brand (every programmer should read it):

“Convention is preferable to law, being more adaptive, accommodating, and locally appropriate, but a fast-moving society outruns the pace of formal convention and must resort to abstract law.” (p. 74)

He goes on to point out:

“In New York City the cost of getting permission for remodeling often exceeds the cost of the work itself.” (p. 75)

We, the programmers, web designers, and the customers that drive the process, are that “fast-moving society” outrunning the pace of convention. And the huge number of APIs, frameworks, databases, etc. – and the various associated rococo towers of functional calls parameters – are our volumes of “abstract law.” And, as in New York City remodeling, this results in a situation in which even the cost of even the simplest deviation from plan or maintenance can quickly exceed the value of the system.

The world of built architecture saw this coming before we recognized it in the world of virtual architecture. For decades now, architects like Christopher Alexander have been pointing to the need for an architecture based on placing person, place and convention over abstraction, an in implementing this architecture via modular design patterns. (Christopher Alexander is considered the Ur-father of the software design pattern movement. Every literate programmer should investigate his books on built architecture as well.)

And now, at long last, these same ideas are beginning to have an impact on the software world.

(Although not the sole or even the first proponent of such notions, I think a lot of credit has to be given to the Ruby on Rails community. If you’ll forgive the pun: Ruby is such a rich programming language, perhaps it needed a simple setting.)

As I think back to my own experience in this regard, it reminds me of the Zen proverb:

When I began my journey,
mountains were mountains and trees were trees;
During my journey,
mountains were no longer mountains and trees were no longer trees;
When I returned from my journey,
mountains were once again mountains and trees were once again trees.

When I first started programming, I simply wrote what was needed in the most direct way. I didn’t concern myself with structure or architecture or refactoring or mirroring existing interfaces. I churned out some pretty amazing stuff (for a beginner) during the brief intervals between classes, etc., but the results were a maintenance nightmare.

Then, I discovered the idea of formal programming and API design. I slavishly architected each program, strove to complete the design before beginning coding, etc. I tried to mirror existing APIs, providing things like formal copy constructors and comparison even where they were unlikely to be needed, etc.

But now, I once again see the wisdom of simplicity. Not the wild, unconstrained simplicity of the beginner, that stems from exuberance and ignorance. But the measured simplicity that comes of having repeatedly stumbled during my journey and having been burned one too many times from self-created complexity.

Below is a brief roster of programming practices and other ideas which encourage simplicity. Some of these have links to Wikipedia just to get you started, but remember that search engines are your friend.

Convention over configuration – the idea that the conventional use of a thing is also made to be the default. That the most likely use should also be the easiest, simplest, most natural, and most likely to guess.

Convention over code – the finer-grained sibling of convention over configuration. One example is languages that allow one to leave off purely syntactic elements such as braces and semicolons where they can be inferred.

Principle of least astonishment – stuff works as expected based on similar instances one has encountered. The cold water tap is on the right and has blue cap and the hot water tap is on the left and has a red cap.

Law of Demeter – stuff that is closer together talks in more detail than stuff that is far apart; stuff talks in preference to other stuff nearby and does not “jump the chain of command.” For example, it is reasonable that instances of classes within a library will share more knowledge with each other than with external classes. (Although the “one dot” principle does conflict with some functional programming best practices.)

The purpose of a system is what it does – and not how well it follows some abstract rules. However, keep in mind that cost-effective maintenance and extension are important parts of the purpose of most software systems. A related principle is that, to an experienced programmer, the intent of the program should be easily discernible from the code.

The Peter Principle – software tends to exist at a level of complexity just beyond the skill of the programmer who wrote it. This unfortunate situation results from two goals that – taken in isolation – are laudable: the goal of a programmer to push the limits, and the goal of a client to get the best immediate value. The key is to know when to stop: keep the ordinary stuff manageable, and save the boundary-pushing for those situations that really require it.

Principle of long term laziness – pretend that you, rather than the client, will have to pay the price for maintenance. When that inevitable bug shows up at six p.m. on Friday, which would you rather fix: the most complex piece of code you’ve ever written, or the simplest.

REPL is your friend – and if your environment doesn’t support formal REPL, favor experimentation by writing lots of small, easily testable blocks. The resulting code is almost always simpler and more maintainable. (This is also a binding principle in Christopher Alexander’s view of built architecture.)

The world is built of small, simple, reusable things. A lot of ancient architecture is lost forever because people pulled out the stones and bricks to make new buildings. The mighty palace or fortress became a ruin, but its components lived on in the form of simpler houses, stone walls, and pathways.

Software is the same way. The mega application or whiz-bang web page you’re so proud of today is tomorrow’s ruin. The basic classes and simple code fragments that make it up will be remembered, copied-and-pasted, and will live on. Build yourself bricks and tools, and use them to make house after house.

-Neil

No comments: