Journal Articles

CVu Journal Vol 31, #1 - March 2019 + Design of applications and programs
Browse in : All > Journals > CVu > 311 (6)
All > Topics > Design (236)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: The Simple Life

Author: Bob Schmidt

Date: 05 March 2019 17:36:44 +00:00 or Tue, 05 March 2019 17:36:44 +00:00

Summary: Pete Goodliffe urges us to keep code simple.

Body: 

Simplicity is the ultimate sophistication.
~ Leonardo da Vinci

You’ve heard the advice before: ‘KISS.’ Keep it simple, stupid. Exactly how stupid do you have to be to get that wrong? Simplicity is an undoubtedly excellent objective; you should certainly strive for it in your code. No programmer yearns to work with overly complex code. Simple code is transparent; its structure is clear, it does not hide bugs, it is easy to learn, and easy to work with.

So why isn’t all code like that?

In the developer world, there are two kinds of simplicity: the wrong sort and the right sort. The ‘simplicity’ we are looking for specifically does not mean: write your code the easiest way you can, cut corners, ignore all the nasty complicated stuff (brush it all under the rug and hope it goes away), and generally be a programming simpleton.

Oh, if only it were that easy. Too many programmers in the real world do write ‘simple’ code like this. Their brain does not engage. Some of them don’t even realise that they’re doing anything wrong; they just don’t think enough about the code they’re writing, and fail to appreciate all of the inherent subtle complexities.

Such a mindless approach leads not to simple code, but to simplistic code. Simplistic code is incorrect code. Because it is ill-thought through, it doesn’t perform exactly as required – often it only covers the obvious ‘main case’, ignoring error conditions, or it does not correctly handle the less likely inputs. For this reason, simplistic code harbours faults. These are cracks that (in the typical simplistic-coder way) tend to get papered over with more ill-applied simplistic code. These fixes begin to pile on top of one another until the code becomes a monstrous lumpy mess; the very opposite of well-structured, simple code.

Simplicity is never an excuse for incorrect code.

Simple code takes effort to design. It is not the same thing as overly simplistic code.

Instead of this wrong simple-minded ‘simplicity’, we must strive to write the simplest code possible. This is very different from disengaging your brain and writing stupid, simplistic code. It is a very brain intensive pursuit – ironically, it’s hard to write something simple.

Simple designs

There is one sure sign of a simple design: the fact that it can be quickly and clearly described, and easily understood. You can summarise it in a simple sentence, or in one clear diagram. Simple designs are easy to conceptualise.

Simple designs have a number of notable properties. Let’s take a look.

Simple to use

A simple design is, by definition, simple to use. It has a low cognitive overhead.

It is easy to pick up because there is not too much to learn at first. You can start working with the most basic facilities, and as you need to adopt the more advanced capabilities, they gradually open up like a well-crafted narrative.

Prevents misuse

A simple design is hard to misuse and hard to abuse. It reduces the burden on the code’s clients by keeping interfaces clean and not putting an unnecessary burden on the user. For example, a "simple" interface design will not return dynamically allocated objects that the user has to manually delete. The user will forget. The code will leak or fail.

The secret is to place the complexity in the right places: generally hidden away behind a simple API.

Simple designs aim to prevent misuse. They may involve extra internal complexity in order to present a simpler API.

Size matters

Simple code minimises the number of components in the design. Big projects with many moving parts may justifiably require a large number of components; it is possible to have many flying parts and be ``as simple as possible.’

Simple designs are as small as possible. And no smaller.

Shorter code paths

Remember the famous programmers’ maxim: every problem can be solved by adding an extra level of indirection? Many complex problems can be subtly masked, and even caused by, unnecessary levels of indirection hiding the problem. If you have to follow a long chain of function calls, or trace indirected data access through many levels of ``getter’ functions, forwarding mechanisms, and abstraction layers, you will soon lose the will to live. It’s inhuman. It’s unnecessarily complex.

Simple designs reduce indirection, and ensure that functionality and data are close to where it’s needed.

They also avoid unnecessary inheritance, polymorphism, or dynamic binding. These techniques are all good things, when used at the right times. But when applied blindly, they bring unnecessary complexity.

Stability

The sure sign of a simple design is that it can be enhanced and extended without massive amounts of rewriting. If you continually end up reworking a section of code as your project matures, then you either have a ludicrously volatile set of requirements (which does happen, but is a very different problem) or you have an indication that the design was not simple enough in the first place.

Simple interfaces tend to be stable, and don’t change much. You may extend them with new services, but do not need to rework the entire API. However, this should not be a straightjacket: interfaces need not be set in stone. Don’t make your code unnecessarily rigid – this itself is not simple.

Simple lines of code

Simple code is easy to read, and easy to understand. It is therefore easy to work with.

Personal preference and familiarity tends to determine what makes individual lines of code look simple. Some people find that certain layout idioms help clarify their code. Others find those same idioms a huge hindrance. More than anything else, consistency leads to simple code. Code with widely varying styles, naming conventions, design approaches, and file formats is needlessly obfuscated.

Consistency leads to clarity.

Do not write needless obscure code for any reason: not for job security (we joke about this, but some people truly do), not to impress your colleagues by your coding prowess, and not to try out a new language feature. If you can write an acceptable implementation in mundane, but clear, coding style then do so. The maintenance programmers will thank you for that.

Keeping it simple, not stupid

When you encounter a bug, there are often two ways to address it:

This latter option is the goal. It does require more effort, but boiling the code down to its simplest form pays off in the long run.

Apply bugfixes to the root cause, not where symptoms manifest. Sticking plaster symptom-fixes do not lead to simple code.

Assumptions can reduce simplicity

Invalid ‘simplifying’ assumptions are easy to make when you’re coding and, whilst they can reduce the complexity in your head, they tend to build into twisted logic.

Simple code does not make unnecessary assumptions, either about the requirements or problem domain, about the reader, about the runtime environment, or about the toolchain used. Assumptions can reduce simplicity, as you implicitly require the reader to know extra information to make sense of the code.

Avoid implicit assumptions in your code.

Assumptions can, though, increase simplicity. The trick is to make it clear exactly what assumptions are being made; for example, the constraints and context that the code is designed for.

Avoid premature optimisation

Optimisation is the antithesis of simplicity. Knuth, in his 1974 Turing Award lecture ‘Computer Programming as an Art, famously said: “Premature optimisation is the root of all evil (or at least most of it) in programming.”

The act of code optimisation is generally that of taking a straightforward, readable, algorithmic implementation and butchering it: pulling the algorithm out of shape so that it executes faster on a given machine under particular conditions. This inevitably alters the shape to be less clear and, therefore, less simple.

Write clear code first. Make it complex only when needed.

Employ a simple, standard sort, until you know you need to make it cleverer. Write the most straightforward implementation of an algorithm and then measure to see if you need to make it faster. Again, beware of making assumptions: many programmers optimise the parts they think will be slow. The bottlenecks are often elsewhere.

When working at the design and architecture level, you can make decisions that have real impact on how your system performs. You might consider the decisions made here a form of optimisation. When you get to the code level, these architectural concerns are not easily reversible, and you can try to enhance code performance with low-level tweaks. These tweaks tend to reduce clarity, and obfuscate the code logic in order to eke out a few CPU cycles.

Sufficiently simple

Simplicity is allied with sufficiency. This works in a few directions:

Only write as much code as is needed. Anything extra is complexity that will become a burden.

A simple conclusion

We all know that beautifully simple code is better than needlessly complex code. And we’ve all seen our fair share of foul, ugly, complex code. Few people aim to write code like that. The road to complexity is usually trodden with hurried changes and slipping standards. Just one slack change. Just one sticking plaster fix. Just one code review skipped. Just one "I don’t have time to refactor." After enough of these, the code is an unholy mess, and it’s hard to figure a way to restore sanity.

Sadly, simplicity is pretty hard work.

Simplicity is a banner born out by many popular developer maxims: YAGNI – you aren’t going to need it – speaks to the theme of sufficiency. DRY – don’t repeat yourself – speaks to the theme of code size. Our preference for high cohesion and low coupling speaks to simplicity in design.

Questions

Pete Goodliffe Pete Goodliffe is a programmer who never stays at the same place in the software food chain. He has a passion for curry and doesn’t wear shoes.

Notes: 

More fields may be available via dynamicdata ..