The command line interface (CLI), sometimes referred to as a terminal, has been around for decades. It’s a powerful interface for working with computers. Arguably the most prevalent CLI comes in the form of a “shell” on your computer, such as bash on macOS or Linux, or PowerShell on Windows (but it supports bash now, too!).

The CLI is often contrasted with that of a graphical user interface (GUI), where objects are displayed on screen and manipulated through keyboard and mouse. Many usability and UX practitioners have created heuristics, or “rules of thumb”, to quickly assess the usabiliy of a graphical UI. I think it would be beneficial to apply the same heuristics to the CLI, keeping in mind they usually do have very different target audiences.

Numerous lists of heuristics exist, such as the 10 usability heuristics from Jakob Nielsen, Ben Shneiderman’s Eight Golden Rules of Interface Design, and Don Norman’s Principles of Design.

For this very informal heuristic evaluation, I’ll use Nielsen’s list and apply it to the standard bash shell flavored CLI available inside macOS’s Terminal.app.

Visibility of system status

The system should always keep users informed about what is going on, through appropriate feedback within reasonable time.

The CLI fails miserably at this rule. By default, the CLI rule is “silence is golden” and output of even simple commands like “cp” and “mv” (copy or move a file, respectively) does not provide output unless an error happens. The assumption is “no news is good news”. Occasionally a command will provide a flag for a “verbose” mode where every action is printed out, but verbosity is rarely the default.

Match between system and the real world

The system should speak the users’ language, with words, phrases and concepts familiar to the user, rather than system-oriented terms. Follow real-world conventions, making information appear in a natural and logical order.

The CLI does this really well if you consider the “users’ language” consisting of advanced concepts like processes, pipes, and file descriptors. I think as programming becomes a more ubiquitous activity the audience will grow and more novices will enter the field, diluting the narrow range of the “users’ language” to something that is less tied to the underlying OS, filesystem, and architecture. Furthermore, he last part is of note: information does not necessarily appear in a “natural and logical order”. Some commands are flexible (see the next principle) and accept flags anywhere (thanks git!) and others are less forgiving and expect a (somewhat) arbitrary order to their arguments.

User control and freedom

Users often choose system functions by mistake and will need a clearly marked “emergency exit” to leave the unwanted state without having to go through an extended dialogue. Support undo and redo.

Users of the CLI often tout its endless flexibility, for example being able to chain commands through the pipe ⎮ command. This gives users a great level of control and freedom. There is rarely an extended dialogue in normal operations. Want to remove a file? “rm file” and it’s gone, no dialogue at all…no confirmation either, though (see Error prevention below).

You can easily redo commands on the command line, but undo is a thorny subject. Did you “rm -rf folder” by accident? Sorry, that’s gone, you’re a professional that should have known what you were doing. People are fallible, though, and our systems need to be built to take this into account. For example, the Amazon S3 outage illustrates that even the most professional professionals make mistakes: “Unfortunately, one of the inputs to the command was entered incorrectly[…]”. All systems can benefit from having some level of undo support, especially with something like the CLI that provides so much freedom.

Consistency and standards

Users should not have to wonder whether different words, situations, or actions mean the same thing. Follow platform conventions.

Let’s look at the abbreviated usage for some popular CLI commands (these were taken from the man pages of macOS):

  • grep [options] [pattern] [file …]
  • find [options] path [express]
  • ls [flags] [file …]
  • mv [flags] source target
  • cp [flags] source target
  • ln [flags] source target

There is largely consistency here in the syntax of the commands where the command name is first, followed by some options or flags, and then a path you want to operate on. File/folder manipulation commands like mv, cp, and ln take source and target in the same order. Many of these commands are standardized because they have been around for decades and, in the examples here from macOS, are from the BSD General Commands Manual. If you look at the man page for ls you’ll even see a Standards section; it confirms to “IEEE Std 1003.1-2001”.

So, as far as consistency and standards go, I think the CLI meets this usability criteria most of the time. I don’t have any good examples of inconsistency for the CLI shell itself, but there are plenty to be found if you compare CLI-based text editors like nano and Vim. Heck, a million developers have had trouble exiting Vim, so it obviously violates some usability criteria. ;)

Error prevention

Even better than good error messages is a careful design which prevents a problem from occurring in the first place. Either eliminate error-prone conditions or check for them and present users with a confirmation option before they commit to the action.

The CLI assumes you know what you’re doing, so there are very few safeguards, even for commands that can lead to catastrophic errors. I would hazard a guess that rm -rf is the most-discussed command that leads to errors. It forcibly removes a directory and all its contents and does not prompt you (unless there is a permissions problem) for confirmation. So, no error prevention there which, coupled with poor undo support, can lead to really devastating results.

Recognition rather than recall

Minimize the user’s memory load by making objects, actions, and options visible. The user should not have to remember information from one part of the dialogue to another. Instructions for use of the system should be visible or easily retrievable whenever appropriate.

Out of the box, *nix flavors of the CLI do a really poor job of this. By default, everything is invisible until you ask for it. If you’re in a folder like your home directory, you may be able to see which user you are and the computer name:

[user@computer ~]$

The command prompt can be customized to make more things visible, but I doubt most non-hardcore developers or sysadmins bother.

Recall is heavily emphasized in the CLI. Can’t remember that argument sequence for the “tar” command? You have to use the man command to pull up its manual page and decipher the help text there. Didn’t know manual pages exist? Well, nothing in *nix is going to show that to you.

Flexibility and efficiency of use

Accelerators — unseen by the novice user — may often speed up the interaction for the expert user such that the system can cater to both inexperienced and experienced users. Allow users to tailor frequent actions.

This is where most CLIs really shine. There are tons of hidden commands and features available to make things faster for expert users. Through shell scripts, repetitive tasks can be automated. Technically, you could probably even alias the rm command to something that would prompt you before deleting a folder to prevent some errors, but that requires a good amount of knowledge of the CLI and shell you’re using.

Aesthetic and minimalist design

Dialogues should not contain information which is irrelevant or rarely needed. Every extra unit of information in a dialogue competes with the relevant units of information and diminishes their relative visibility.

Another place where CLIs really shine. You can’t get much more minimalist than a single cursor at a command prompt in a sea of black/white/or whatever your preferred background color is. I would argue, though, that this minimalism comes at the cost of violating many of the principles listed here (control and freedom, error prevention, etc).

Help users recognize, diagnose, and recover from errors

Error messages should be expressed in plain language (no codes), precisely indicate the problem, and constructively suggest a solution.

There are three parts to this heuristic:

  1. Express an error in plain language
  2. Precisely indicate the problem
  3. Suggest a solution

CLIs usually fail to address numbers 1 and 3, and only sometimes do they address number 2 in a meaningful way. For example, attempting to copy a file you do not own results in:

cp: file.txt: Permission denied

In this case, the error does actually have plain language, but the problem indicated is not precise enough–which permission is the problem (owner, group, user)? Here there definitely is not a suggested solution, probably because the implied solution is to fix the permission problem and there are several different ways to go about this. Nonetheless, not even one solution is suggested. What if it offered something like this:

cp: file.txt: The file cannot be copied because a different user is the owner. Since you are in the sudoers list, you should be able to do this with "sudo cp file.txt file2.txt"

Here a solution is suggested with some intelligence built-in; namely, determine if the user is capable of performing a “sudo” command and tell them they can try that instead. This still adds an extra step, though; if we know the user is in the sudoers list shouldn’t we just perform the action for them anyway? This gets into some tricky territory in the *nix permissions model and I don’t want to go down that rabbit hole in this post.

git addresses all three well in some instances; for example:

CONFLICT (content): Merge conflict in $fileName
Automatic merge failed; fix conflicts and then commit the result.

Help and documentation

Even though it is better if the system can be used without documentation, it may be necessary to provide help and documentation. Any such information should be easy to search, focused on the user’s task, list concrete steps to be carried out, and not be too large.

CLIs based on *nix ship with “man” pages. These are searchable thanks to other commands (i.e. “grep”). They are focused on the commands themselves, though, and not the “user’s task”.

When the man pages aren’t helpful, thankfully there is a whole community of support at StackOverflow.

Perhaps if CLIs followed some of the above heuristics more there wouldn’t be nearly 58,000 questions tagged with “shell” on StackOverflow. 😉