Command-line Interface Antipatterns

Apps (web, mobile, and native) and APIs do not lessen the need or importance of command-line tools. A beautifully designed command-line user experience is arguably more important and than a graphical UX because the command line is deceptively simple: text on a screen. Wielding mere text on screen is an art akin to wielding mere paint on canvas. This page documents what I believe to be command-line interface (CLI) antipatterns which, in the long or short run, detract from both UX and developer effectiveness.

Car-Tree

A command-line tool must always exit zero on success. I don’t know how old this standard is, but it probably predates the discovery of fire. Woe unto developers who violate it because other tools presume this standard. On failure, exit 1 is most common, and there are a few special exit codes to avoid.

“Success” is often not trivial. If there’s a warning, does that mean the tool was not successful? One way to help decide is to think of the caller: other tools or scripts using the tool. To the caller, exit zero means “continue executing”. This is what Bash -e is used for: stop executing on the first non-zero exit status. So exit zero if, despite a warning, it’s ok for the caller to continue with reasonable assurance they, too, will be successful. When in doubt, exit non-zero.

Car-tree

I call this antipattern “car-tree” for an analogy I use to describe ignoring non-zero exit status: you crash a car into a tree, but instead of stopping you back up, put on a blindfold, then floor it, racing off broken and blind until you run off a cliff. Don’t endanger yourself or others by violation this simple standard: exit zero on success, and stop executing on non-zero exit.

Poor Standards

Poor or incorrect use of STDOUT and STDERR is an abomination to command-line tools. There are few Unix standards and expectations more venerable and proven than printing normal program output to STDOUT and error (and maybe warnings) to STDERR. The following should always hold true:

$ tool >/dev/null
$ echo $?
0

No output to STDERR should always indicate success, as further indicated by a zero exit (see first antipattern: Car-Tree).

–quiet

First, presuming --quiet means “no output”, this “feature” is solved at fundamental level with STDOUT and STDERR redirection:

Output Solution
Errors to screen, discard output >/dev/null
Errors to screen, output to file >file
Errors to file, discard output >/dev/null 2>file
Discard errors and output 2>&1 >/dev/null

Second, the level of quietness is never right. For some people it’s too quiet, for others not quiet enough.

Third, does --quiet apply to STDOUT and STDERR, or just the former? See previous point.

Fourth, the developer will soon regret the option when it comes time to implement it because the code will be littered with if conditionals like:

if !quiet {
   print("Midway upon the journey of our life", +
         "I found myself within a forest dark," +
         "For the straightforward pathway had been lost.")
}

Or the developer will create and pass around a printing object to all parts of the code. And usually some line is overlooked and a bug is file: “–quiet does not suppress all output.”

Given the first point (that this feature is already thoroughly solved by STDOUT/STDERR redirection), an explicit --quiet option needs a very necessary and convincing reason to exist for it not to be an antipattern.

–verbose

A --verbose option shares two of the same problems as --quiet: too verbose for some people, and not verbose enough for others; and it litters the code with if conditionals or an object to handle printing.

Verbosity is more difficult to get right than quietness because --quiet usually means “no output to STDOUT”, but there are no standards or norms for verbosity. Consequently, no two programs’ --verbose look even remotely similar. Moreover, verbosity is sometimes a repeatable option, like -vv for “very verbose”, which complicates the option further.

Rather than --verbose, the developer probably wants log levels, for which there are standards, like syslog severity levels and others.