If learning to code were like learning to write…

If learning to code were like learning to write, we’d start with words, first teaching children what a token is and how to read them. “Madison, look at that billboard, see the ‘if’? That’s a token in a lot of programming languages.” “Daniel, did you know that the numbers you practiced writing in kindergarten today are called ‘integers’? Python has integers too. They’re a sequence of digits. Want to go tokenize some digits while we shop at the farmers market?”

If learning to code were like learning to write, we’d move onto sentences, teaching children how to parse statements. “Look Madison, I brought you a new book from the library called ‘Python and other beasts’. Let’s read the first page: ‘print(‘ssssssss!’)’. Can you read those tokens? What kind of tokens are they? That’s right, an identifier, a parenthesis, a string, and another parenthesis. Together, they make a function call, which has a name, and a list of arguments. Good job!”

If learning to code were like learning to write, we’d next teach children how to read sentences, showing children how computers execute them. “Want to play computer Daniel? I got a new board game for us. It’s like the game Simon Says: you read a statement and you try to do what the computer is going to do. If you do it exactly like the computer does, you stay in the game, but if you do something different, you’re out. Check out this card, it says: ‘secret.index(“treasure”)’. Want to play?”

If learning to code were like learning to write, we’d next teach children how to read short books, giving them programs to read, exposing them to all of the computational possibilities of the language they were learning. “Madison, what did you choose for your book project this month? Oh, an Instagram post indexing algorithm, interesting! Are you liking it? What’s your favorite idiom?”

If learning to code were like learning to write, we’d ask children to start writing sentences, creating simple statements that accomplish small tasks. “Daniel, we keep forgetting to turn the light off in the garage. Can you log into the IoT portal and write a rule that turns it off every night at 9 pm?”

If learning to code were like learning to write, we’d then ask kids to write short essays, scaffolding their problem solving with design patterns for various genres of computational problems. “Okay, before you leave, let’s discuss your homework for this week: I want you to write a simple Python script that takes President Trump’s Twitter feed and finds all tweets that denigrate a person, place, or thing. I’ve provided the list of tweets that do this, so your job is to find an algorithm to do this automatically. This is classification problem, so the last two weeks of content should set you on a good path.”

If learning to code were like learning to write, kids would go to college and participate in writing workshops, doing code reviews with each other to improve their encapsulation, clarity, performance, and other software qualities. “Madison, I really like this abstraction here; its so simple, but you’ve managed to use it everywhere, reducing a lot of boilerplate and probably preventing a lot of defects. I bet if you added some polymorphism to this function you could simplify it even further”.

Unfortunately, with today’s computing education, learning to code is nothing like learning to write. Computer science never teach students about words. They give a few examples about grammar, but nothing comprehensive. They never teach students how to read, expecting them to pick it up independently. Then, on week two of intro CS, they immediately ask students to write whole essays. Its no wonder so many students are overwhelmed and drop out.

Programming languages are the least usable, but most powerful human-computer interfaces ever invented

Really? I often think this when I’m in the middle of deciphering some cryptic error message, debugging somme silent failure, or figuring out the right parameter to send to some poorly documented function. I tweeted this exact phrase last week while banging my head against a completely inscrutable error message in PHP. But is it really the case that programming languages aren’t usable?

Yes and no (as with all declarative statements in tweets). But I do think that only some of these flaws are fundamental. Take, for example, Jakob Nielsen’s classic usability heuristics, one rough characterization of usability. One of the most prominent problems in user interfaces is a lack of visibility of system status: usable interfaces should provide clear, timely feedback about the how user input is interpreted so that users know what state a system is in and decide what to do next. When you write a program, there is often a massive gulf between the instructions one writes and the later effects of those instructions on program output and the world. In fact, even a simple program can branch in so many ways that some execution paths are never even observed by the programmer who wrote the instructions, but only by whoever later executes it. Talk about delayed feedback! There are whole bodies of literature on reproducibility, testing, and debugging that try to bridge this disconnect between command and action, better exposing exactly how a program will and will not behave when executed. At best, these tools provide information that guide programmers toward understanding, but this understanding will always require substantial effort, because of the inherent complexity in program execution that a person must comprehend to take action.

Another popular heuristic is Neilsen’s “match between system and the real world”: the system should use concepts, phrases, and metaphors that are familiar to the user. There’s really nothing more in opposition to this design principle than requiring a programmer to speak only in terms that a computer can reliably and predictably interpret. But need to express ideas in computational terms is really inherent to what programming languages are. There are some ways that this can be improved through good naming of identifiers, choice of language paradigm, and a selection of language constructs that reflect the domain that someone is trying to code against. In fact, you might consider the evolution of programming languages to be a slow but deliberate effort to define semantics that better model the abstractions found in the world. We’ll always, however, be expressing things in computational terms and not the messy, ambiguous terms of human thought.

Programming languages fail to satisfy many other heuristics, but can be made significantly more usable with tools. For example, error prevention and error actionability can often be met through careful language and API design. In fact, some might argue that what programming languages researchers are actually doing when they contribute new abstractions, semantics, and formalisms is trying to minimize errors and maximize error comprehensibility. Static type checking, for example, is fundamentally about providing concrete, actionable feedback sooner rather than later. This is very much a usability goal. Similarly, Nielsen’s “recognition rather than recall” heuristic has been met not through language design, but carefully designed and evolved features like autocomplete, syntax highlighting, source file outlines, class hierarchy views, links to callers and callees in documentation, and so on.

There are other usability heuristics for which programming languages might even surpass the usability of their graphical user interfaces. For example, what user interface better supports undo, redo, and cancel than programming languages? With modern text editors and version control, what change can’t be undone, redone, or canceled, at least during design time? Our best programming languages are also perhaps the most consistent, flexible, minimalist, and beautiful user interfaces that exist. These are design principles that most graphical user interfaces struggle to even approach, as they often have to make sacrifices in these dimensions to achieve a better fit with the messy reality of the world.

So while programming languages might lack usability along some dimensions, but with considerable effort in careful tool design, they can approach the usability of graphical interfaces (partly through the use of graphical user interfaces themselves). In fact, there’s been a resurgence of research inventing precisely these kinds of tools (some by me). These usability improvements can greatly increase the accessibility, learnability, and user efficiency of languages (even if they only ever approach the usability of graphical user interfaces).

Now to the second part of my claim: are programming languages really the most “powerful” user interfaces ever invented? This of course depends on what we mean by power. They are certainly the most expressive user interfaces we have, in that we can create more with them than we can with any other user interface (Photoshop is expressive, but we can’t make Photoshop with Photoshop). They might also be the most powerful interfaces in a political sense: the infrastructure we can create with them can shape the very structure of human communication, government, commerce, and cultural production.

But if by power we mean the ability to directly facilitate a particular human goal, there are many tasks for which programming languages are a terrible tool. Sending emails, having a video chat, playing a game, reading the news, managing a to do list, etc. are activities best supported by applications explicitly designed around these activities and their related concepts, not the lower level abstractions of programming languages (or even APIs). In this way, they are probably the least powerful kind of user interface, since they really only facilitate the creation of directly useful human tools.

If there’s any truth to the title of this post, its the implied idea that programming languages are just another type of human-computer interface and the rich and varied design space of user interface paradigms. This has some fun implications. For example, programmers are users too, and they deserve all of the same careful consideration that we give non-programmers using non-programming interfaces. This also means that programming languages researchers are really studying user interface design, like HCI researchers do. There aren’t two fields we might find more dissimilar in method or culture, but their questions and the phenomena they concern are actually remarkably aligned.

For those of you who know me and my work, none of these should be surprising claims. All of my research begins with the premise that programming languages are user interfaces. It’s why, despite the fact that I principally study code, coding, and coders, I pursued a Ph.D. in HCI and not PL, software engineering, or some other disciplined concerned with these phenomena. Programming languages are and will forever be to me, the most fascinating kind of user interface to study and reinvent.