When I was a child, we had a Compaq 286 computer with a green-and-black screen. It didn’t have a mouse, of course; instead, you had to type commands for everything. There were still games for computers in those days, and I loved playing them. With the help of a book of 100 games, in second grade I started writing my own in BASIC, one of the earlier programming languages. I would type the game codes into the computer, run the program, and play the game. Although this process generated a game I could play, it was a terrible way to learn to program: Imagine trying to learn a language by copying text from a book, then watching the movie.
I could see what all the commands meant cumulatively, but I had no idea what each individual piece did. When I began to modify my games a bit, to see whether I could change the rules to win more—like most kids, I wanted to win—I started to understand what the piece I changed used to do by seeing what happened after I altered it. This was still an ad hoc way to learn, but over the next ten years I became a skilled programmer.
When I began teaching, I noticed that my students thought they should memorize and reproduce code, a method reminiscent of my old read-and-copy days. After an exam a student complained that I had asked them to write code they’d never seen before. But of course, the point is to write code to solve new problems: I wouldn’t ask them to rewrite an essay they’d completed already for homework. I realized that they weren’t understanding programming as a language.
While programming languages don’t have the usual sentence structure, the punctuation is odd, and the text isn’t arranged into paragraphs, there are still rules governing the grammar of the language. You use the language to convey your ideas, and every large, sophisticated work is formed from small pieces whose meanings combine together.
And as with spoken languages, you can’t learn to write until you learn to read.
When I talk about reading code, I mean being able to take a piece of code and figure out exactly what the computer does with each line—reproducing the computer’s behavior step-by-step. For an inexperienced programmer, reading code involves executing each line and updating a diagram of the program’s state (values of variables, etc.) at each step. In spoken language this is much like a beginning reader “sounding out” a complex word. It takes similar time and effort to learn a computer language as the programmer learns to apply the rules. But the result is a deep, fundamental understanding of how the language works, how it can be manipulated and used in endless combinations.
Once my students can read code in this way, they soon come up against the realization that the gulf between a problem statement and the code to solve that problem is quite large. A grade- schooler learns to write a paper by breaking down the assignment into steps: Write a thesis statement, draft an outline, refine the outline with more detailed points, write a topic sentence for each paragraph, fill in the text. The conceptual distance from one step to the next is shortened, and pieces eventual fit together as a seamless whole.
Code works the same way—and for the same reason: logic. The hard part about writing a paper is not putting the words on the page, or even following the rules of grammar, but figuring out which words to put on the paper, what idea you want to express, and how you want to support it. The programmer must figure out when to write a ‘for loop’, an ‘if’ statement, or declare another variable. Understanding the syntax and semantics of the language leads to fluency, which means you can make a computer do what you want it to, and with that comes freedom of self-expression.
Hilton is an assistant professor of the practice in Duke’s department of electrical and computer engineering and department of computer science. He received the Klein Family Distinguished Teaching Award in 2015. Access to his method of teaching computer programming is available through his textbook, All of Programming.