Blog Stats

In my last post I stated that the only guideline for writing good software is to make it simple. There are a number of very specific code analyses that can be applied during a code review that help guide towards that goal (see Clean Code). In addition to those code analyses there are also a whole family of rules and anti-patterns focused solely on Simplicity. These are worth learning so that they can be recognised and corrected when they begin to bleed into your work.

KISS

The grand-daddy of all these rules is KISS. This is an acronym for “Keep it simple, stupid”. This principle states that simple systems work better than complicated ones.

The term comes from the US Navy in the 1960s. The term “stupid” doesn’t refer to the engineers implementing the solution; it refers to those people that will be tasked to maintain the system later. You want to keep a system so simple that even stupid people can understand it.

Clever Code

The term “clever” is usually used sarcastically to describe code. “Clever” usually describes very terse code that solves the problem in a non-obvious way.

“Clever” is used as the antonym of “stupid” from the “keep it simple stupid” rule. Only clever developers will be able to maintain the code, not average or stupid developers.

It is better to write code that is embarrassingly obvious as to what it is doing (Declares Intent) as opposed to clever.

Golden Hammer

“Golden Hammer” is an anti-pattern. It is usually expressed with the phrase, “if the only tool you have is a hammer then every problem starts to look like a nail”. This warning is important to have on this list because it is very important to not confuse the terms “simple” and “familiar”.

Sometimes KISS can be used to impede learning and experimentation. The argument is that that the “simplest” solution is just to do the same thing we did last time. Although that is familiar, it is not necessarily the simplest solution.

For example someone that this very familiar with assembly programming may believe that writing a program in assembly is easier that writing the same program in a object orientated language (with the learning that implies).

Sticking with the familiar can put you in a rut. If not corrected this can lead to obsolescence.

Although the Golden Hammer anti-pattern teaches us that learning and experimenting on a project is a good thing, too much of this can be bad. Beware of creating artificial and chaining dependencies on your project.

For example, I’m already to release my new software build. However, before I release I want to test it on that new operating system. In order to do that I’m going to have to setup a virtualization server. But I don’t know how to do that. No problem, there is a conference on that in Elbonia next week. However, they use yak hair as currency there and I have none. So, I’m at the zoo shaving this yak, and why? Because somehow in my mind I’ve built up an artificial dependency that shaving this yak is a requirement before I can release my software.

Before adding dependencies (and especially dependencies of dependencies) on your project, make sure those dependencies are inline with the overall vision for your project, product, or career. If those dependencies add significantly more work than the golden hammer, consider reducing their scope. You don’t want to completely eliminate all opportunities for learning, but you don’t need to get a doctorate degree either. Just take an opportunity to improve a little on every project.

YAGNI

Another acronym rule is YAGNI, “you aren’t gonna need it”. The simplest way to implement something is to not implement it at all. The faster you realize this is the correct course of action the more time you’ll save. And, it’s always 100% bug free.

Premature Optimization

Premature Optimization is an anti-pattern derived from applying the YAGNI rule to performance of the system. Systems need to be able to perform at speeds that are tolerable to users, no more. Beta or proof of concept systems may not even need that.

It is possible to spend weeks of development to squeeze just a few milliseconds of savings out of a program. Make sure this is worth it.

Worse Is Better (New Jersey Style)

Worse is Better (aka the New Jersey Style) is a philosophy proposed by Richard Gabriel that states that is better to have a simple system over a correct one. It is presented as an alternative to doing “The Right Thing” (aka the MIT approach).

Really what this philosophy is supporting is an iterative approach to software development. It is better to get into production a simple system that will handle 99.9999% of customer transactions rather than spending much more time building a complicated system that could handle every transaction.

Furthermore, supporting complicated customer scenarios might make the system harder to use for simple transactions. Adding 100% correctness in the system could hurt overall usability.

Trying to design and implement a perfect system can be a futile, never ending, never delivered endeavour. In the real world there is no such thing as perfect. Simple can be a better, more realistic target.

Big Design Up Front

The “Worse Is Better” philosophy applies to both design and implementation of a system. If this is limited to just design, “the right thing” forms the anti-pattern “Big Design Up Front”. In this scenario people spend a long time attempting to design every aspect of a system before any implementation or delivery is done. Instead of multiple, incremental releases all the functionality is attempted to be delivered in a single release. The large scope of the one and only development cycle makes the development process complicated.

Bike Shedding (Parkinson’s Law of Triviality)

Parkinson’s Law of Triviality notes that more people are willing to spend more time arguing over small, trivial systems than large, complex ones. The example given why this is the case is a comparison of building a nuclear reactor as opposed to a bike shed. The design concepts needed for building a nuclear reactor are complicated, therefore there are fewer people able to constructively criticize this. However, the design of a bike shed is easy to understand, therefore it invites more criticism.

The effect that this has is that even small components of a system, which should be simple, can be turned into complicated ones due to endless meetings and design reviews.

Writing software can be complicated. It almost always takes some refactoring and management to turn complicated systems into simple ones. Hopefully this partial list of Simplicity principles and anti-patterns can serve as a reference guide for a daily reminder to strive towards simplicity.

Feedback

I remember reading something like this about 10-15 years ago while I was caught in the death grip of an evil IBM waterfall project. It was a joyous, refreshing, sane perspective that made COMPLETE sense to me. That was the beginning of my love affair with agile development practices. However, looking back, I would say I was terribly naive and really didn't understand what these concepts really meant and how to *safely* apply them. When misapplied these can be very dangerous.

Based solely on my own personal experiences, I would say that 5-10 years ago you would run across many over engineered systems that did not apply any form of KISS or YANGI at all. These systems were built before the agile movement and that was almost certainly part of how they ended up as they did - big, upfront design was in their blood. In more recent years (post agile movement) I've seen far more of the opposite - woefully under-engineered systems. It's a tough balance to strike, but tis the job of the architect. I've seen KISS and YAGNI misapplied to the point where systems are horribly brittle, untestable, and have no thought to what the business needs might need to envolve into. What complicated things further is "simplicity" is VERY subjective. Ask two developers if LINQ queries are more simple or more complex than just writing a for loop... you will get two polar opposite answers. And you don't have to search far to find a developer whose idea of simple is a 2000 line static void Main() function.

All this is highly dependent on the types of systems you're building of course. If you're building short lived or relatively static line of business apps where the usage patterns are fixed, then yeah... just get it working. Who cares? If you're building commercial software where the code will be living potentially decades and evolving dramatically in features with unpredictable usage patterns (i.e. if thing this gets hot we could get spikes of 1000 TPS overnight), then that's a different story. I'm almost never a fan of big upfront design, but for systems like that you are foolish to not lay down some architectural foundation and do so with an eye for anticipating *potential* future change. To do that effectively and efficiently, you need to understand your business domain well, be a student of time tested patterns, and leverage tools to lay down such patterns and refactor into patterns where needed. In the end, you need to have software that's easy to change. It's as simple as that... which is incredibly difficult. Blind application of YAGNI, KISS and no-big-upfront-design can quickly doom your system if tomorrow's business changes result in an effective full-rewrite.

Big Upfront Design is never needed (short of building a spaceship) but upfront design is always needed. It is to be proportional to the scope of the project. A software architect who is worthy of the title can appropriately design a project in about 3% of it's total time.

A medium sized project that will take approximately 16-24 man-months can be designed in approximately 2-3 weeks. A prototype that will be completed in several man-months should be able to be designed in a week or even a day.

Design time does not include requirements gathering, that is not part of design. Requirements gathering is independent of design. A design is nonsensical to have without requirements.