A tourist stops a musician on the streets of New York. “Excuse me, can you tell me how to get to Carnegie Hall?” “Of course”, answers the musician, “Practise, practise, practise!”
In the book ‘Outliers: The Story of Success’, the author Malcolm Gladwell repeatedly mentions the “10,000-Hour Rule”, claiming that the key to achieving world class expertise in any skill, is, to a large extent, a matter of practising the correct way, for a total of around 10,000 hours. This is based on a study by Anders Ericsson. (Wikipedia)
In most ways, the software engineering industry is an unmitigated disaster: churning out enormous quantities of unreliable and insecure software of little to no use to an audience who has been trained to expect nothing of any quality. In this post I want to focus on the particular disaster that is how its inhabitants practise software engineering.
Whilst by day I am a software engineer, in my spare time I’m a musician: I play the horn in a large number of groups throughout London and further afield. I’ve been playing the horn since I was 9 years old. I probably play about 12 hours a week on average: I’ll have two or three rehearsals on evenings during the week, maybe including a concert (these will be about 3 hours each), and then on days when I don’t have group rehearsals, I’ll do about an hour’s private practice at home. Based on this, I’m probably well past the 10,000 hour mark, though I’m not the same quality as a professional horn player, and I don’t make my living from playing the horn.
Practice at home is much more intense than ensemble rehearsal: you often work on very difficult studies and exercises, the purpose of which is to develop technique: flexibility, accuracy, stamina, tuning, tone production, rhythm (in no particular order). You deliberately try to work on studies that you cannot play correctly yet (for however you want to define “correctly”). If you spend an hour a few times a week working slowly and diligently on the bits you can’t play then you gradually improve and hopefully this positively influences the rest of your playing. Ensemble rehearsals will usually involve music that is much easier, but what you’re focusing on is playing well with others, learning what to hear and react to, and how the conductor (assuming there is one) wants to influence your playing of your part.
There is no expectation that you will be able to play a piece correctly the first time. If I’m booked for a gig then I’ll find out as soon as possible what the programme is and if I have any concerns about the pieces, I’ll make sure I’ve seen the music, and listened to a recording, and practised the part in advance of the first rehearsal. As you get invited to play with better and better ensembles this becomes increasingly important: firstly they’ll often be playing harder and more interesting music, and secondly there’ll often be much less rehearsal time. If you turn up and can’t play the music and hold up the rehearsal, you won’t be asked back. So you take personal responsibility for making sure you’re sufficiently well prepared. The balance is very much you spend the majority of your own time making sure you are in good enough shape to play the music, and a completely minimal amount of time rehearsing and performing with others.
As an aside, one of the biggest issues with music industry is the word “play”. Musicians are not “playing”, they’re “working”, every bit as hard if not harder than many other professions. Use of the word “play” creates the impression that professional musicians are doing this for “fun”, and don’t have rent and bills to play. If the software industry suffered from the same chronic oversupply of talented engineers and virtually non-existent salaried positions as music does, I would suggest the quality of the output of the software industry would rapidly improve.
In software we don’t have a concept of practice, and we’re generally very bad at effective learning. When we write something, we expect we’re going to get it right. This is absurd, and the evidence is that we never get anything completely right (or even mostly right). The pressure to deliver a product by a certain date means there is never enough time to consider everything (or even anything) in sufficient detail, and so any significant piece of software is always shipped with vast numbers of bugs. With the increasing disaster that is the “Internet of things”, it’s only a matter of time before this approach directly ends up costing a large number of people their lives.
When we look at code we tend to look at bad examples rather than good, and we don’t tend to have any idea how to evaluate code (I have written previously on the inadequacies of testing). Jacob Kaplan-Moss gave an excellent key-note talk at PyCon 2015 on this area, and as usual there’s a good write-up on the talk on LWN. As he says: “We are infants in figuring out how to measure our ability to produce software”. We look at failure as something that’s generally unacceptable but we define it in such narrow terms that it’s completely nonsensical: the truth is that we’re all failing most of the time. That’s perfectly normal though: most people in most industries “fail” most of the time too, they just don’t look at it with such a binary mindset.
As a musician, if I listen to recordings of live performances (unedited), and it’s a piece of music I know very well, I’ll often be able to spot all sorts of mistakes that occur. This can initially be depressing (especially if it’s a recording of me!) until you realise that in live performances, mistakes happen all the time, the audience almost never notices during the concert, and nearly every recording you buy of any piece will have been edited and patched up. Even for “live” recordings, the orchestra will often have done a one-hour patch session after the concert is over (and the audience has left) to fix any bits that went too wrong.
Musicians often learn through contrast with positive examples. For example, when you have a lesson with a teacher (and I still do sometimes), they are frequently showing you what works for them. They’re not showing you examples of failed musicians. There will often be identification of what you’re doing “wrong”, but this is often just “different” to what the teacher wants you to do. Sometimes there will be health issues (“if you continue with that technique you’ll eventually damage these muscles permanently”), but the message there is always “you probably want to consider doing this instead”. A lot of the time, beyond a certain level, it’s more “have a think about doing it this way and you might find it easier”. Contrast this with software where we couch everything in “right” and “wrong”. We show bad examples of code to mock and humiliate, often without the guidance of “try this approach instead”.
Musicians are of course not perfect. There are countless examples of musicians who have had to abandon their chosen career after sustaining extensive injury. It’s often said that young musicians when studying at music college or university are training basically as hard as Olympic athletes. But unlike athletes, there is often very poor understanding of what your body can cope with and how to effectively train. The mantra of “if I don’t do 5 hour’s practice a day I’ll never be any good” is pervasive, even if slowly students start to recognise that marathon runners don’t train by attempting to run a marathon every day.
Software engineers however never seem to expect to need to practise at all, let alone for five hours a day, and I think this needs to change. It is interesting to think about what practising might look like. For athletes and musicians, practice has some form of repetition. Professionally, software engineers don’t like to have to rewrite something several times, thought that’s almost always necessary for anything non-trivial. We tend to try to blame the customer for changing requirements or not being clear instead of accepting rewriting code is an essential and healthy process. The desire to avoid rewriting can lead to overly general solutions to problems: if I make this code incredibly general then I’m sure it’ll be right even if I’ve not quite understood the requirements completely. I’ve certainly been frequently guilty of this throughout my career. The added complexity of the more general solution is rarely a good idea. Equally, my experience of rewriting the same piece of code two or three times is that on each iteration the code gets simpler and smaller, more correct, and easier to learn and maintain.
A friend of mine was telling me he wanted to play around with writing a SAT solver. These sorts of toy projects can be an excellent way to learn and practise something, and I can think of several approaches that probably don’t work well.
Go and read some papers on SAT solvers. This probably won’t help because you’ll likely find the papers dense with terminology and concepts you don’t yet understand. After reading them all, you’ll still probably have no idea where to start actually implementing.
Try to find a tutorial and follow that. I think these can be fairly useless because to a large extent you’re just copying and pasting code. Unless it has exercises for you to do, you can follow through such tutorials without thinking and so you don’t really learn anything. They’re often written in the style of an experience report rather than actually being a tutorial. Of course if you force yourself to think about the code you’re pasting, and to experiment with it, then you can make your own exercises and this can work, but tutorials would certainly benefit from being designed to have exercises.
It might be more productive to just churn out some code without reading anything. This will serve at least a couple of purposes: firstly the code will reflect your understanding of the problem you think you’re trying to solve. Getting that information out of your head and down on the page is often very useful as it’ll give you something concrete to refer to when you later learn something that adjusts your understanding of the problem: you’ll (hopefully) clearly be able to see where your previous thinking diverged from your new more-informed thinking. Secondly, you’ll get stuck but then probably have some more precise questions to search for answers to and you may be able to identify common terms or structures between your own work and information you then uncover. Thirdly, you’ll actually be writing some code and hopefully be going through the usual write-compile-run-debug cycle which I think is generally good practice.
It’s worth remembering that musicians and athletes practise things they know they can already do. Just because I know I can play a D-minor scale doesn’t mean I don’t do it every day. If you stop practising certain things you get worse at them. So does that mean that every day we should do warm ups at our desk implementing (every day!) quick-sort, breadth-first-search, skip-list, and Paxos? Maybe; I’m not sure. It would certainly be interesting to see how quick you could get at it and how long until your answers stopped changing! But I think by not doing so, there’s a danger of losing confidence in building and manipulating basic data structures, even in just manipulating pointers correctly.
There are lots of times where I think having confidence with implementing basic data structures is very beneficial. I don’t particularly think complexity-classes (big-O notation) is one of them – at least not frequently. One example that does spring to mind though are the opposite pulls of reimplementation versus dependencies. Both are evil and should be avoided, but you normally need to make a well-judged choice between them. Being able to quickly sketch out how you would implement something gives you clues about how much of that library you would actually end up using, and how much code you’d end up writing if you implemented it yourself. Always reimplementing everything isn’t going to be very efficient, but always reaching for a library is going to cause a whole host of different problems. If you’re not well practised at implementing little things I would bet there’s a tendency to reach for libraries a little too easily, and a tendency to not think “well, how is this implemented?”. Treating any library as if it’s magic is a quick path to ruin.
So what do you think? If there was going to be “brain-training” or dual n-back for programmers, what would it look like? As I said, as a musician, I practise by learning to play hard studies that I certainly can’t play to start with. What does that look like for a programmer? Should it be something closer to the ICFP Programming Contests? Spend an hour a day on one of those and switch every month to a new one?