An emerging theme
I recently went to the ACCU 2011 conference, for programmers who believe in ‘professionalism in programming’. There was an eclectic programme of talks, but I was struck by how often the presenters had something interesting to say about one topic in particular: simplicity. Rather than write up the individual presentations, I thought it would be more interesting to write about what was said about simplicity.
Giles Colborne set the tone early in the conference with his talk titled Advanced Simplicity. I only caught this talk on video after the event, but I’m glad I did. It ties in with a number of later talks that I did see in person.
Giles pointed out that there is another simplicity on the other side of complexity. In other words, it takes skill and hard work to design a simple product.
Much of the talk concerned the difference between a mainstreamer and an expert. Experts have a very different mindset; for example, for most people, reading the manual first is perverse behaviour! Giles produced this table to describe this difference between the mindsets:
|Precise control||Ease of control|
|Take it apart, explore||Afraid of breaking it|
|Exact match||Good enough|
|Detailed mental model||Loose mental model|
|Deferred gratification||I want it now!|
|Read the manual||What does RTFM mean?|
Complexity is the reason that computers appear to break when you’re stressed. For example, a large complex print dialog might be okay most of the time, but when you’re in a hurry it becomes incomprehensible.
Giles listed four ways to simplify something:
Remove. Although Joel Spolsky said in 2006 that the act of adding new features is what had the most impact on their bottom line, people are now choosing software from reviews by people like them instead of by looking at bullet points in feature lists. Don’t take away too much, though.
Organise. This is really cheap! Group things together, highlight important things, etc. Where possible, match your interface to the patterns already in people’s heads.
Hide. One unsuccessful example of this is Microsoft Office’s auto-hiding menus, which created a ‘shifting’ user interface. Don’t hide stuff and then put up a big sign saying ‘stuff hidden here’! Hiding is a good technique when things are hidden right up until you need them.
Displace. (Also known as cheating!) For example, move remote control buttons to an on-screen menu. You can also displace complexity to the user, but you have to trust them.
Key practices to cope with complexity
The second session specifically about simplicity/complexity was a series of lightning talks on Key practices to cope with complexity.
Michael Feathers was first up. He said that complexity was often the result of rationalist bias – of believing in a simple model. If it ‘looks good in the model’, it is probably hiding complexity in the implementation.
He made a good point that people have different productivity at different times of day, but that most businesses don’t treat this as a complex system (for example, by forcing everyone to work the same core hours or by judging productivity by number of hours worked).
A key point was that we should all keep better records in order to learn more about our systems. The thing that will bite you is the thing that looks simpler than it is!
Klaus likened software systems to an ant hill, where you can’t see the complexity inside.
He also made the point that software engineers are control freaks (and perhaps this is what attracted them to the profession in the first place). In contrast, managers are scared of complexity because they associate it with a lack of control. It is certainly interesting to look at the forces at work in software projects from this point of view.
His key point was that you can choose where you want your situation to be complex and where you don’t. In other words, you can set a defined level of ignorance, but you need integration to learn when you have ignored too much. You can’t reduce intrinsic complexity; you just have to choose what to accept. I think there are some interesting parallels here with outsourcing.
Klaus said that ‘some developers revel in producing Baroque code’. Or, quoting Stalin, ‘Quantity has a quality of its own.’
More generally, the key skill is in seeing the abstraction that is necessary to make things as simple as possible.
Peter said that having your ‘object glasses on’ was not always the best way to design a system. The key to abstraction is getting rid of classes. Indeed, he said that class hierarchies are suspicious! His mantra was: ‘Just get rid of stuff.’ Imagine moving apartment every three months – you could only do this if you pared down your possessions to the bare essentials.
He said it is possible to write code that is mainly linear, and allow polymorphism to handle the other stuff.
Kevlin was up next. His proposition was that: ‘Our imagination takes us away from where we should be looking, into a world of created complexity… There is enough detail in the problem without imagining more.’
When going back to some code later, we can ask: ‘Why was this here: because we didn’t understand it or because we did?’
Kevlin referenced the topic Use Uncertainty as a Driver from the 97 Things Every Programmer Should Know book, which claims that when a choice needs to be made in software design we shouldn’t rush in and choose an option; instead, we should take a step back and ‘design so that the choice between A and B is less significant’.
As he said, tongue in cheek, it’s fun to create complexity, and it also offers job security!
Kevlin also came back to the theme of simplicity in his lightning keynote Cool Code, saying that elegance is simplicity. For example, JUnit made testing sexy, but it’s very simple code.
Pete was up next, continuing his mission of ‘making the world better one line of code at a time.’
He said that complexity was other people’s code, to which I’d add that it’s also your own code from the past.
Although size isn’t the enemy, there are positive consequences of breaking code up: better comprehension, easier modifiability and not harbouring future complexity.
It’s important to have clear roles and responsibilities: if a class does A and B, it’s probably wrong. Wherever possible, remove the code [tick!].
Objects cyclically calling each other is an anti-pattern. Typically, you can add interfaces to decouple the objects.
In summary, it’s usually the people writing the code who make it complex.
Jon’s first point picked up on Pete’s last one: things are the way they are because they got that way (over time).
He made an important point that complexity is not a thing, it’s a relationship.
Most of Jon’s talk was about the softer skills needed for handling complexity. For example, learning helps you cope with complexity. It’s also important not to rush; as Gerry Weinberg said, ‘Spend a fifth of your time doing nothing.’ Peter Taylor expanded on this theme in his talk The Art of Productive Laziness.
Jon made an interesting analogy: when you’re making a cake, only the last stage looks like a cake. I choose to interpret this as meaning that if you take good ingredients, prepare them carefully and use quality tools and equipment, your gooey mess will turn into a tasty treat!
Some thoughts on specific programming languages
It was somewhat ironic that a conference so closely linked with C++ should be so strongly in favour of simplicity – as noted by Scott Meyers, Linus Torvalds was already complaining that C++ was too complicated in 1992, before the STL came into being, before template meta-programming, and before all the additions to the language proposed for C++0x.
Of course, Scott said that, despite its complexity, C++ is still a popular language. It has several things going for it: it is multi-paradigm, has a commitment to systems programming and has a fundamental compatibility with C (which has been the most popular programming language for three decades).
Every few years, typically after reading an article about how Lisp is ‘the one true language,’ I wonder whether I should learn it. Then I take a look at some example code and the syntax frightens me off. I get parenthesis blindness. One of the talks I attended was given by Didier Verna about Common Lisp. This was an interesting session and was a good demonstration of the purity and power of Lisp. (A particular nugget was that Lisp programmers have no need for XML or domain-specific languages, since Lisp itself fulfils both these needs. Also, the top-down functional decomposition approach causes useful reusable functions to drop out, almost as a side-effect.)
The syntax is still not to my taste, though. Coming back to the theme of simplicity, the conciseness of functional programming languages can actually harm their simplicity, in my view. Simplicity is subjective. In other words, there is a tension between simplicity from the computer’s point of view and the programmer’s.
I want to write code that’s easy for me (and other programmers) to understand, even if the computer has some extra work to do. Perhaps it’s my writing background, but I want to write code that I can read back like a story. I have a hard time with curly braces and semicolons getting in the way, so how can I reconcile myself to all these parentheses? Aesthetics are important.
Some thoughts about unit testing
This section is not directly related to discussions from the conference, but is something I’ve been thinking about for a while and is related to simplicity.
Does adding a test simplify the codebase or does it make it more complicated? I’m not thinking here of complexity to the computer (e.g. cyclomatic complexity or some other measure of algorithmic complexity), but of complexity to the programmer.
To argue that all tests add complexity because they are contributing lines of code is naïve. If a test demonstrates how the system should behave, and does so in an easier-to-understand way than inspecting the code directly, then isn’t it making it simpler? When trying to understand what a function does, isn’t it easier to look at its tests and design contracts first? That’s one reason why I like Cobra as a language: it ties these things to the code, rather than treating them as second-class citizens to be hidden away in a separate file.
If you don’t know why something is there, it will never be safe to change it. The code says what happens, the tests say what should happen, the comments say why it happens this way.
Simplicity is also about using the right tool for the job. Gavin Heavyside’s presentation on Nosql databases included a discussion about graph-based databases, which are well suited for working with network-like data structures. Like OO, this is an instance of simplicity coming from a resonance between the problem and solution domains.
In Nosql databases, you don’t need to specify a schema up front. Instead, you can store new data as it becomes available, adding it in whatever structure you wish. Indeed, one of the questions that Gavin asked was: ‘What if there could be millions of columns?’ That is a question that can’t be asked of traditional relational databases.
But which is simpler? The answer probably depends: if the data will always fit on a single computer then a tried and tested relational database is probably simpler; if you’re dealing with terabytes (or more), or need to distribute it for speed or robustness then Nosql is likely to be simpler than trying to manage the distribution of data between relational databases.
Thoughts from the bar
A benefit of the ACCU conference is that with so many interesting people gathered in one place, you are just as likely to gain inspiration from a chat over a pint at the bar as you are from one of the talks.
One topic that came up in discussion in the bar was how we – IT professional all – approached simple Windows tasks in different ways, such as using different shortcut key combinations for certain commands. There were several ‘I didn’t know you could do that!’ moments along the way. The relevance to the theme of this post is that even the platform and tools we use are complex, even for experts. So simplicity of user experience is important, too.
In Good Enough is the enemy of Good, Steve Freeman said that, particularly in startups, they’re good for a couple of development cycles, then they ‘hit the complexity wall’. In other words, if you don’t spend time trying to do things properly, the technical debt will get out of hand.
Simplicity is not the easy option: it is a constant struggle against various forces with a tendency to make code more complex over time.
I know I have been guilty in the past of pursuing interesting complexity when well-trodden simplicity would have been more appropriate. The valuable lesson is to seek to derive as much satisfaction from a job well done – a good, clear, concise and maintainable solution, which doesn’t necessarily break new ground – as one gets from exploring the cutting edge. This is a good fit for ACCU’s tagline of professionalism in programming, so it is an apt theme to have emerged from this conference.