Programming Errors For Alle

Last week, interaktionsfaggruppen went off to Hornstrup Kursuscenter in Vejle and together with teachers of datalogi and multimedia from as far away as Aalborg, we explored – on the course “Programmering For Alle” what it means to teach programming, hearing about both formal research findings, and personal experiences, many of which were provided by the other course participants. It was an excellent course, presented by two extremely knowledgeable teachers (Michael E. Caspersen and Jens Bennedsen), who were so well-equipped with theory and practical experience that I am sure it will take me some time to digest and integrate it all. Fortunately, the course fell in the middle of our semester planning, so it was hungrily received, and we have been able to make practical use of many of the ideas immediately. I also have the slides and a ‘compendium’ of articles and papers to chew upon, if I need reminding, when other matters crowd in.

Once nice thing is that we at KEA are doing a number of things ‘right’, although it’s very interesting to find out why they are right, especially when we have arrived at those approaches intuitively. We also learned that some pedagogical approaches, such as problem-based learning, are especially poor ways of teaching programming. (This is not to say that problem-based learning has no value, just that teaching programming is better achieved via other methods).

In all there were many useful insights, and even some formal methods that may be used to clear up various mysteries in the mind of the novice programmer. Such mysteries are typically experienced as “what do I need to do?” or “it doesn’t work, and I don’t know why”, or “I’m stuck, but I can’t remember what I have tried, because I erase whatever I do as soon as the program tells me there is an error”, or variants. How do we get the student to accomodate errors as ‘business as usual’ in the programming process?

One of the most valuable insights I got from the course, which I would like to share more broadly with the readers here, is the value of ‘live coding’, and the special value of making mistakes ‘in public’. The reader is also referred to my recent post here about self­-censorship and knowing ‘the right thing to do’.

The typical way that programming is taught (in books, classrooms or lectures) is to show some correct code, ask the student to copy it (or reproduce it from memory) and then hope that they find all the errors before lunch. If they don’t, in a classroom situation, the teacher is somewhat obliged to run around and help the students fix the errors (help!!!).

This approach seems obviously correct for various reasons even though the results are so often incorrect and undesirable: We want to show them how to do ‘the right thing’. However, the students usually only find out how to ‘do the thing right’, and consequently have difficulty applying their knowledge more broadly. Doing the right thing and doing the thing right are not the same, and the optimal educational approach should cultivate a sense for both, and some way of distinguishing which is which, so that both the right thing is chosen, and it is done right.

Any student (or head of department, or minister of education) may question the special usefulness of introductory programming courses when there are so many ‘good’ books around, and it’s true – if you know a certain amount about programming already, it’s relatively easy to shift from one language to another with the help of a decent book, but the problem is getting people to that level of learning in the first place – a process which is almost entirely absent from programming literature, and – it must be said – absent from many courses.

Some programming books claim that it has been “made easy” or can be achieved “in 21 days”, by “visual learners” (how flattering!) or even that it’s SO easy that even “dummies” could do it. And indeed these books often include a series of useful metaphors and carefully constructed steps from the most simple code snippets, up to a fully functioning ‘example’. Some of these books even make elaborate efforts to make the material ‘fun’ and accessible by including cartoons, speech bubbles, 1950s stock images and even anthropomorphised conversations between software objects (”Flash is at a bar, talking to the browser, and Flash says…”). All of these things may be useful, but they almost entirely omit the ‘rubber-to-road’ process of programming:

  • Actually dreaming up new code,
  • Having some kind of idea of what you’re doing with it,
  • Knowing where to write it, i.e. knowing where not to write it,
  • Knowing roughly what code to write first, i.e. knowing what code to leave til later,
  • Knowing how to go about fixing the inevitable syntax errors,
  • Knowing how to go about finding the inevitable logic errors (remember, finding is the same as fixing if it’s a logic error, because you’re fixing the logic in your own mental model),
  • Knowing – now that it compiles and runs – what parts to change to make it even better,
  • …and finding out what parts of your mental model need to be updated before your greater goal may be achieved. (The greater goal may indeed be more programming, in which case we re-enter this paragraph da capo.)

What IS included in the books is syntax and API stuff. How to make ‘for’ loops, how to express inheritance, or how to load and display an image, and so on. There may even be an elaborate, but mostly irrelevant example, intended to show the ’special issues’ presented by more complex scenarios. Sometimes there will be a few pages about using the debugger, but it’s “click this button to advance in the code” without any clue about what kind of context “advancing in code” might make any sense within.

Programming books are often branded together in series such as “… in 21 days”, “Head First…”, “in a Nutshell” and so on. This has always struck me as disingenuous. I’ve always wanted to see a series of programming books with the brand title of “… by trial and error”, or even better “… by mistake”. Imagine, “Learn ActionScript 3.0 by mistake”, or perhaps even “Teach yourself wireless networking by accident”. It makes the learning process sound inevitable, rather than something you have to set aside time for.

Despite some obvious attractions, “10 years younger in 10 weeks” is really not as desirable as “10 weeks younger in 10 years”. (This joke does not work when translated into Danish). Similarly, learning things ‘by mistake’ is always going to be less effort – and probably more effective – than learning them ‘on purpose’, even if it is only a purposeful “21 days”. Put simply, the books are going about it wrong. As living, breathing, fallible creatures, we teachers of programming are in a premium position to make the learning process more intuitive, not by telling the students what to do, but by indicating what invisible obstacles are ‘out there’, and the most natural way to do that is to bump into those obstacles in front of them – to allow room for error in the live coding situation.

It is suggested, then, that the students learn better about the practice of programming by watching the teacher write the code ‘live’ (for example, projected on screen) and – crucially – that the teacher should make ‘typical mistakes’ along the way. The best thing about ‘typical mistakes’ is that they do not need to be planned for. They happen quite by themselves, which is why they are ‘typical’. (They are generated by the logical configuration of the system – oh! I forgot the closing parenthesis. Silly mistake. Easily corrected. So easy, that even YOU could do it!

This is not to say that the teacher should make mistakes on purpose. I mentioned before Bateson’s and Soros’ observation that conscious purpose often prevents predictable success. Certainly ‘deliberate mistakes’ risk having an artificial or even pretentious quality, and may entirely rule out certain kinds of knowing. A coquette pretending to drop her handkerchief by accident might attract attention, but it leads only to knowing of a given kind. The authentic mistake is more valuable as a teaching example precisely because it is unplanned – because it allows the student to observe how the teacher ‘recovers’ from the error, which is an extremely valuable lesson, omitted from most books.

The teacher can cope with typical mistakes, but lives in dread of unexpected or unusual mistakes. The bad thing about unexpected or unusual mistakes is that you don’t know what they are until they have happened. They emerge from an epistemological blindspot, which may be covered or negotiated using a certain amount of anxiety or nervous energy.

Worse still, if the error emerges from the depths of the unknown, there may be no obvious solution. This is regarded as a pedagogical abomination by teachers of practical matters, because we are very much focused on showing people “how to do things”, rather than “how to get stuck”. This bias is sometimes imagined to be ‘more effective’, but that’s just an excuse: Given that the fear of speaking in public is the most common phobia of all, getting stuck while speaking in public is possibly one of the most embarrassing things that can happen. It’s also one of the most hilarious, but the joke may only be understood outside the context of any particular classroom session, for the same reason that it’s not funny if the clown laughs at his own deckchair routine – suddenly it becomes a painful tragedy for the embarassed audience, and what on earth can they learn from that?

To avoid this anxiety as far as possible, the teacher will often take special care to learn to avoid the ‘typical’ mistakes which may occur. “If I can’t guard against embarrassing, unexpected screw-ups, I can at least avoid the most obvious howlers”.

The teacher may become very skilled at this, so skilled, in fact, that he starts to unconsciously avoid making any of these typical mistakes when doing ‘live coding’ in front of the class, and he may even get to the point where he forgets to mention that he might have made those mistakes, often, once upon a time… Naturally, all the students who are not informed of the ‘internal landscape’ that the teacher is tacitly negotiating while writing ‘nice code’ effortlessly, and before their very eyes, and they all follow the logic of the demonstration as best they can, but have no idea why, when the programmer could write almost anything at all, he writes precisely this and that. Furthermore, the students have no idea what else he could write, and precisely why he does not write those things instead.

When the teacher gets sufficiently good at presenting this elaborate fiction, he may even feel competent enough to write a book about ‘programming’, leaving out all the ‘typical mistakes’ that were so useful in his own learning process. The most expert practitioners may even be in the worst position to teach what they know, because so much of their skill and knowledge has become unconscious. At Hornstrup we were presented with the example of the student learning about hammers, saws and drills and then being presented with a beautifully crafted mahogany cabinet and told “now you do it”. Cabinet making is never taught like that. Why then do we continue to teach programming that way?

Watching a demonstration of programming which does not include any errors, is no more informative than watching a trained elephant paint a convincing likeness of an elephant. We learn almost nothing of how the process came to happen, and what purpose it might serve (beyond our own amusement), nor whether the elephant can also paint a picture of a giraffe, or render the mood of his dreams in a series of gouache-on-velvet vignettes.

The problem is not learning what to do. The problem is knowing what not to do. If you know what not to do, you must also have learned something about the set of possibilities you have available to you: The symbol ‘G’ is undefined in hexadecimal, just as the symbol ‘A’ is undefined in decimal. If you’re writing colour codes, you ‘intuitively’ leave out the letter ‘G’, for the same reason that you leave out the letter ‘A’ when you are filling in a time sheet. Nobody ever mentions the letter ‘G’ in relation to hexadecimal, because it’s simply not in the set of symbols used. Even so, you know to avoid it. The novice does not know this kind of stuff, and is just as likely to write “G” as anything else, just as they may as well try to write all the code in one function, rather than break it into smaller parts. And so on.

The most useful question which any student may ask, therefore, is “why couldn’t you do X instead of Y?” Any student, at any level of knowledge, could ask this question, because any student would be in a position to observe the proceedings and choose any item for Y. The questions themselves may be read as a map of the contours of understanding and attitude in the student. “Why couldn’t we use square brackets there instead of parentheses?” tells us one thing about the student’s level, and “Why is that function defined as private instead of public?” tells us something quite different.

As the class of X approaches the class of Y, the student can be guided to home in on the ‘correct’ (i.e. preferred/celebrated) knowledge. Fortunately, the respective classes of X and Y would never be wildly different. (I have had students in my HTML class who didn’t know how to rename a file, but that is a system error of a different kind).

You wouldn’t expect “Why couldn’t you do it as an Excel macro instead of writing a package definition?” or, “Why couldn’t you go screw yourself, instead of declaring max_speed as an unsigned integer?” (This last example would be an interesting act of sabotage, worthy of further consideration, and perhaps a drink at the Friday bar), no – the class of X and the class of Y are usually similar, if not exactly the same. In some cases, the answer is “You can indeed do X instead of Y, but then you’d have to do Z as well, which you haven’t learned yet”.

The teacher, it is only fair to say, should have an answer for every one of these questions, because the student is not only providing a critique, but also an alternative. She is required to make a choice of alternative, which requires that she gives some thought to the extremely relevant matter of what set of alternatives are available, here and now – to commit herself, as it were, to a given logical level of understanding. The teacher should not necessarily be required to make time to answer these questions, because X might mean “shut up while I talk”. (i.e. the teacher/student power relation is necessarily asymmetrical, and the student implicitly agrees with this by being present), but the student should be encouraged to think as freely as she likes about the value of X, and – especially – why it might be better or worse than Y.

Writing all the code correctly, first time round, is ‘faking it’, like the painting elephant, and likely to cultivate undesirable and/or unrealistic attitudes to the matter of learning programming. The student watching the teacher produce perfect code is invited to see it as some kind of magical ‘gift’ that some got and some don’t got, and besides “I’m really bad at anything like this, my maths teacher told me so”.

But preparing for a lesson by rote-learning of what code to write to achieve a given task is not just faking it, it is almost certainly counter-productive. It’s like selling a ballet performance to people who are expecting physiotherapy. By all means, prepare the example – have the relevant data ready (images, sound files, videos, the ‘theme’ of the task, the target group etc.), and have a clue about what kind of programming approach you are going to take, but don’t prepare the code that you are going to show the students first in any great detail. The errors will pop up by themselves, the tricky thing is noticing what it is that leads to a given choice, and mentioning them as if a student asked “why X instead of Y?” – “Oh, I almost forgot to mention… I am using an external CSS file because it will be convenient for me later, when I have to hook it up to the other documents…”. The student doesn’t have to remember any of these reasons, she has only to learn that there are reasons, that programming skill is not a ‘gift’, neither is it magic. It’s something learnable.

When the errors inevitably occur, you can usefully and openly dwell on your own thinking process, and invite responses from the students as to what kinds of solutions may be tried out, guiding their suggestions into the most appropriate domains. With any luck (hey, you’re the expert), you’ll come up with a solution before they do, but you don’t need to get to it immediately. Not when there’s a living, breathing error right in your hands. You’re not going to let it slip away, unlearned-from, are you? Get the cage, a small piece of lettuce and a handful of sawdust! And then sometimes the error is not just an error, it’s a whole pickle, involving not just this small section, but also some other larger problem, which has some larger lesson…

For their part, the students will learn that ‘getting in a pickle’ is a natural and necessary part of the programming process, and that ‘getting out of the pickle’ has some kind of formal structure too. Even if ‘getting out of pickles in general’ represents an infinitely large set of processes, it will soon become obvious that some pickle exit strategies are doomed to fail, whereas others offer better odds. This in itself leads to an intuition about which pickle exit strategies are best for each particular kind of pickle – the kind of thing which is effectively ‘impossible’ to teach in any explicit or purposeful way.


Leave a reply