How Great Programmers Avoid Bugs

Prologue

Some programmers are very good. What makes them so good? In this essay, we look at top programmers to see what techniques we can copy from them and use in our own programming. Try to figure out what things they all have in common.

Bob Martin is a famous advocate of test driven development. His rule: no code can be written without a test. “Every single line of code that you write should be tested.” He builds “a quick, sure, and repeatable proof that every element of the code works as it should.” Proof here means unit tests.

This method is effective, and Martin writes high quality software. Not only does everything get tested, but it's also built in small pieces. With small pieces, it's easier to test every possibility. This is a good way to write software. It's so easy, in fact, that I used to think “small pieces” was the only way to write high-quality code.

Donald Knuth, like a genius, shattered my illusions. He is so confident in his code, that he pays bug bounties from his own pocket; for any kind of bug. Knuth doesn't write code in small pieces. He goes six months or so without testing his code, then he tests it all at once. When he tests, he says, “I get into the meanest, nastiest frame of mind that I can manage, and I write the cruelest test code I can think of; then I turn around and embed that in even nastier constructions that are almost obscene.” At that point, he tries to test everything. Still, the question is raised, how does Knuth keep things straight without testing for so long?

As a general practice, Knuth doesn't prove his code. Instead, Knuth invented literate programming to keep his thoughts clear. He intertwines the documentation with the code, like a story. Literate programming lets him structure the code in a way that is easiest to read. He says, “The extra time I spend in preparing additional commentary is regained because the debugging time is reduced.” Furthermore, he explains: “Structured programming gave me the idea of invariants and knowing how to make black boxes that I could understand. So I had the confidence that the code would work.”

When you work in the Knuth style, you are forced to think of everything that can go wrong, make sure nothing is left out. The style ensures that you've thought of everything, that you have all the corners tied; otherwise you'll have a mess.

So, here we have two programmers with completely different styles. Obviously, both of them have a right answer, because they have good results. There can be more than one right answer. Let's see what we can learn from the next programmer.

Edsger Dijkstra is famous for saying, “If you want more effective programmers, you will discover that they should not waste their time debugging, they should not introduce the bugs to start with.” He wrote a textbook for programming beginners, starting by teaching them how to write proofs.

Some people think that Dijkstra wanted everyone to prove every line of their code, but then halfway through his textbook he wrote, “We shall not formulate any invariants for this program: it is somewhat laborious...it does not really help in developing the program.” He didn't expect people to prove everything.

|[ u, v: int ; u:=y; v:=x ; do x > y -> x:= x - y; v:= v + u □ y > x -> y:= y - x; u:= u + v od ; y:=(u + v)/2 ]|

An example of the programming language used in Dijkstra's textbook

Dijkstra was trying to teach the proving mindset, wherein you try to think of every case and everything that can go wrong. He wasn't trying to dogmatically push people to prove every line of code.

Instead of asking, “Does this code work,” Dijkstra would ask, “Does this code cover every case?” and if convenient, he would prove it.

Epilogue

Three programmers, three different styles of programming. The fun part is to figure out if anything ties them together: is there any technique used by all three? The answer I think, is yes: all three of these programmers (and this is true of every low-bug programmer) have the mathematical mindset, trying to think of everything that can go wrong. They have the proving mindset. They use their own methods to try to detect and fix mistakes, but each has the same goal.

(This shouldn't need to be said, but another thing highly skilled programmers do is try to get rid of bugs. Some programmers don't even try, and it shows in their results).