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.