Maybe we Should Stop Creating Inscrutable CLIs

Sat Aug 17, 2019 | 4 Minute read

In the original Unix tradition, command-line options are single letters preceded by a single hyphen…The original Unix style evolved on slow ASR-33 teletypes that made terseness a virtue; thus the single-letter options.

Eric Steven Raymond, The Art of Unix Programming


Programs must be written for people to read, and only incidentally for machines to execute.

Abelson et. al., Structure and Interpretation of Computer Programs


I just wrote this little bash-ism the other day for removing all attachments from a jira ticket:

jira attach list {{args.ticket}} \
  | cut -sf 2 -d \| \
  | sed 's/ //g' \
  | xargs -L 1 -I % jira attach rm %

It’s a neat little piece of code. I was happy to have it written, but I was also annoyed at how much time I spent in man pages and stack overflow to get it done.

This code is pretty typical for bash, and unless you’ve committed the commands and flags of each command to memory, it’s pretty much impossible to guess what it does. Hold that thought.

Consider another way we might express the same functionality in a language like javascript:

jiraTicket
  .listAttachments({ ticket: "AND-1" })
  .split("\n")
  .map(line => line.split("|")[1].trim())
  .forEach(attachmentId => jiraTicket.removeAttachment(attachmentId));

Imagine someone complains a bit in the a pr that the map line is a bid hard to read. They recommend that the arrow function is given a self-documenting name to address the issue, so you wind up with:

jiraTicket
  .listAttachments({ ticket: "AND-1" })
  .split("\n")
  .map(attachmentInfoLineToAttachmentId)
  .forEach(attachmentId => jiraTicket.removeAttachment(attachmentId));

Now, there are still some “commands” you need to have memorized here. You need to know what map and split means, for example, but I’d like to suggest that it’s much easier to know what this code does as compared to the bash-equivalent above. More generally, we seem to have a higher standard for readibility for non-bash programs than for bash ones.

I’m not going to defend this claim here. If you think typical bash programs and terminal commands are just as readable as the code you write elsewhere on the job, here’s your exit. I might try to argue for this claim with more detail some other time.

The relative inscrutability of the CLIs we use in our terminals has always bothered me. Until recently, I thought the problem was bash itself, but while chatting with some friends, I realized the root of the problem is how we write CLIs.

If you look at all the CLI libraries in various languages, they all make it easy to create commands with UNIX-style, single-character flags that aren’t self-documenting. We discover these libraries and we say to ourselves, “Great! My CLI will work just like all the others.” (I’ve actually thought this to myself before.)

Here’s the problem: all the other CLIs are the opposite of self-documenting: their inscrutable. Why do we want to make another CLI that’s just like all the others? Why do we torture ourselves with inscrutable CLIs?

I’m not going to try to seriously answer that question here. Maybe understanding, using, and creating UNIX-style CLIs is an odd real programmers one-upmanship type of thing. Maybe we just haven’t rethought CLIs since their inception in an environment with slow teletypes that would have make it extremely painful to invoke commands with self-documenting interfaces. Regardless, maybe we should stop writing inscrutable CLIs and apply the same standards of readibility we have in other programming contexts to CLIs and bash programs?

If we did that, maybe the bash-ism for removing jira tickets might look something like this:

jira attach list {{args.ticket}} \
  | extractSubstring --ignoreLinesMissingDelimiter --position 2 --delimiter \| \
  | streamEditor 'substitute/ //globally' \
  | executeWithArgs --lines 1 -substitute % jira attach rm %

Of course, something like this is possible with bash, and many CLI libraries make it easy to use more expressive GNU-style ---prefixed flags. I think we just need to decide that we’re not going to tolerate inscrutable code just because its bash code.

If you’re worried about typing a lot, that’s what autocomplete is for. It’s basically a platitude in programming that most of the time we should optimize for readibility over writability. I’ll end by repeating the Abelseon et. al. quote from SICP:

Programs must be written for people to read, and only incidentally for machines to execute.

Abelson et. al., Structure and Interpretation of Computer Programs


Discussion on HN