I know I’m not perfect, but sometimes I come across software – popular, widely used, well respected software – which has design decisions that baffle me.

An example is Syncthing. The command-line tool for it is syncthingctl, and it uses a mixture of flags and named arguments:

Syncthing control, version 1.3.0

Available operations:
status, -s
  shows the overall status and/or directory/device specific status
    shows overall statistics
  --dir, -d [ID]
    specifies a directory by ID
log, -l
  shows the Syncthing log

  stops Syncthing

  restarts Syncthing

Available top-level options:
--config-file, -f [path]
  specifies the Syncthing config file to read API key and URL from, when not explicitly specified
  default environment variable: SYNCTHING_CTL_CONFIG_FILE
  example: syncthingctl status --dir dir1 --config-file ~/.config/syncthing/config.xml

--api-key, -k [key]
  specifies the API key
  default environment variable: SYNCTHING_CTL_API_KEY

Do you know what syncthingctl doesn’t have? A help command. It does have a -h and a --help, but no help. Maybe they think help is an “option” that modifies other commands; I don’t know, but it’s a bad decision.

Most people who prefer the command line deal with hundreds of CLI commands pretty regularly; there are around 8,000 commands in my $PATH. Around 5,500 have been accessed (used) in the past week, around 2,000 in the past two days. I know I haven’t installed or upgraded anything in the past couple of days, so those accesses are either me or some automated job on my laptop. Assume most of those were done by jobs, I would guess that I use around a hundred tools, with a small core of around 20 regularly. While most use is muscle memory (ls -tlr, tar xf), there’s a second category in my head: flags, named args, and dash use. Many tools – mostly the younger ones without inherited baggage – use args (e.g. hg stat, dinit list, jj files). The older ones use flags (grep -E, ls -tlr). And then there’s the double, single, or no dash variable: depending on ancestry, you’ll have long-form flags with a single dash (e.g. find -type -executable), or a double dash (awk --trace), and rarely - thankfully - no dash (tar xvf). Some commands mix these with wild abandon, but newer ones tend to have some consistency. I have a rattly filing system in my head where commands are in one of these cabinets; it’s tied to the muscle memory, where I remember a couple of arguments, and from that extrapolate the rest. I think of it as a sort of mind map where the lines are sort of fuzzy. Most commands, if they use a single dash for long-form will have a -h or, more rarely, -help. Double dash ones will usually have a --help and -h. Arg-type have a help.

Except syncthingctl. It’s an arg-style, but breaks consistency by putting “help” as a flag. Which means when I use it, there’s always a bit of fumbling to figure out the arguments; the inconsistency is frustrating.

I think people just don’t put much thought into CLIs. There are any number of thoughtful UI design books, essays, blog posts, tooling… but few – if any – talk about CLIs. Or, maybe they just have different opinions; it’s more important that “help” is like other tools, than be internally consistent. I’m pretty sure it doesn’t irritate the authors because they use it enough that they never have to call help.

It seems like I’m picking on Syncthing, but it’s just a small example of a wide problem in CLI tooling. I disagree with Go’s flag library design, which is patterned after the far less popular, less used, Plan 9 OS. Go’s flag library uses the “single-dash-for-long-flag-namess”, rather than the vastly more common getopt style of short -h and long --help options. It’s one of those opinionated choices made for ideology than practicality, a rare thing in Go, and one that punishes the Majority by virtue of being unnecessarily different. But at least, it’s consistent, and I do wish more developers would give their command line UI as much design thought as they would a web or GUI interface. I probably need to pay more attention to that, too.