Journal Articles
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:
- Take the easiest route to solve the problem. Hey, you’re keeping things simple, right? Fix the superficial problem – that is, apply a sticking plaster – but don’t worry about solving any deeper underlying issues if it will be too much work. This is the least effort for you now, but will likely lead to the kind of simplistic code mess we saw earlier.
This doesn’t make things simpler; it makes things more complex. You’ve added a new wart and not addressed the underlying problem.
- Or, you can rework the code so that it accommodates a fix, and remains simple. You may have to adjust APIs to be more appropriate, refactor some logic to create the correct seam for the bugfix, or even perform serious rework because you spot code assumptions that do not hold.
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:
- You should work in the simplest way possible and write the simplest code possible. But keep it sufficiently simple. If you oversimplify, you will not solve the actual problem. Our ‘simple’ solutions must be ‘sufficient’ or they are not solutions.
- Only write as much code as is required to solve your problem. Don’t write reams of code that you think will be useful. Code that is not in use is just baggage. It’s an extra burden. It’s complexity you don’t need. Write the sufficient amount of code. The less code you write, the fewer bugs you’ll create.
- Don’t overcomplicate solutions; excitable developers find this a very real temptation. Solve only the issue at hand. Don’t invent a needlessly general solution for a whole class of problems that are not relevant. Work until you reach a splendid sufficiency.
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
- What is the simplest code you’ve seen recently? What is the most complex code you have seen? How did they differ?
- What sort of unnecessary assumptions can a coder make about their code that will render it too complex? What assumptions are valid to make?
- Is it possible to optimise code but maintain its simplicity?
- Does the ‘simplicity’ of a section of code depend on the capabilities of the programmer reading it? How should an experienced coder work in order to ensure their code is of high quality, but appears ‘simple’ to a less experienced maintenance coder?
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 ..