Hacker News new | past | comments | ask | show | jobs | submit login

Best: probably that of Docker. https://github.com/docker/cli

Why: it didn't force you to read man pages or look up documentation, but instead allowed every command to explain what it does to you, either when you'd run it with --help, or just no parameters (in case it expects any). Furthermore, invocations of these commands weren't just a long string of arguments, but rather commands that are logically grouped and can essentially be navigated as a tree. All of that made it extremely useful and pleasant, at least in my eyes.

It just feels like it's made to actually be used by developers and to help them as much as possible. Whether you agree with me on that or not, i suggest that you have a look at this excellent talk by Dylan Beattie, "Life, Liberty and the Pursuit of APIness: The Secret to Happy Code", which talked more about the discoverability of systems and the developer experience: https://www.youtube.com/watch?v=lFRKrHE8oPo

Nowadays, you can actually use something like Typer for Python to create similarly useful interfaces, which i strongly advise you to have a brief look at: https://typer.tiangolo.com/


  $ docker
  Usage:  docker [OPTIONS] COMMAND
  A self-sufficient runtime for containers
    ... (a list of items)
    -v, --version            Print version information and quit
  Management Commands:
    ... (a list of items)
    image       Manage images
    ... (a list of items)
  Run 'docker COMMAND --help' for more information on a command.
  To get more help with docker, check out our guides at https://docs.docker.com/go/guides/
  $ docker image
  Usage:  docker image COMMAND
  Manage images
    ... (a list of items)
    ls          List images
    ... (a list of items)
    pull        Pull an image or a repository from a registry
  Run 'docker image COMMAND --help' for more information on a command.
  $ docker image pull
  "docker image pull" requires exactly 1 argument.
  See 'docker image pull --help'.
  Usage:  docker image pull [OPTIONS] NAME[:TAG|@DIGEST]
  Pull an image or a repository from a registry

  $ docker image pull alpine:3.15
  3.15: Pulling from library/alpine
  59bf1c3509f3: Pull complete
  Digest: sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300
  Status: Downloaded newer image for alpine:3.15

  $ docker image ls
  alpine       3.15      c059bfaa849c   11 hours ago   5.59MB
The worst: tar

Why: https://xkcd.com/1168/

In short, it's the exact opposite of the previous example. Frankly, without memorizing the flags, i still have no idea how to work with archives with it. Say, i want to create a compressed archive with it.


  $ tar
  tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options
  Try 'tar --help' or 'tar --usage' for more information.

  $ tar --usage
  Usage: tar [-AcdrtuxGnSkUWOmpsMBiajJzZhPlRvwo?] [-g FILE] [-C DIR] [-T FILE]
              [-X FILE] [-f ARCHIVE] [-F NAME] [-L NUMBER] [-b BLOCKS]
              [-H FORMAT] [-V TEXT] [-I PROG] [-K MEMBER-NAME] [-N DATE-OR-FILE]
              ... (a really long list of items)

  $ tar --help
  Usage: tar [OPTION...] [FILE]...
  GNU 'tar' saves many files together into a single tape or disk archive, and can
  restore individual files from the archive.
    tar -cf archive.tar foo bar  # Create archive.tar from files foo and bar.
    tar -tvf archive.tar         # List all files in archive.tar verbosely.
    tar -xf archive.tar          # Extract all files from archive.tar.
   Main operation mode:
    ... (a list of items)
    -c, --create               create a new archive
   Operation modifiers:
    ... (a list of items)
   Local file name selection:
    ... (a list of items)
   File name matching options (affect both exclude and include patterns):
    ... (a list of items)
   Overwrite control:
    ... (a list of items)
   Select output stream:
    ... (a list of items)
   Handling of file attributes:
    ... (a list of items)
   Handling of extended file attributes:
    ... (a list of items)
   Device selection and switching:
    ... (a list of items)
    -f, --file=ARCHIVE         use archive file or device ARCHIVE
   Device blocking:
    ... (a list of items)
   Archive format selection:
    ... (a list of items)
   FORMAT is one of the following:
    ... (a list of items)
   Compression options:
    ... (a list of items)
    -z, --gzip, --gunzip, --ungzip   filter the archive through gzip
   Local file selection:
    ... (a list of items)
   File name transformations:
    ... (a list of items)
   Informative output:
    ... (a list of items)
    -v, --verbose              verbosely list files processed
   Compatibility options:
    ... (a list of items)
   Other options:
    ... (a list of items)
  Mandatory or optional arguments to long options are also mandatory or optional
  for any corresponding short options.
  The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
  The version control may be set with --backup or VERSION_CONTROL, values are:
    ... (a list of items)
  Valid arguments for the --quoting-style option are:
    ... (a list of items)
  *This* tar defaults to:
  --format=gnu -f- -b20 --quoting-style=escape --rmt-command=/usr/lib/tar/rmt.exe

  $ # Copied from the Internet, because the documentation is overwhelming
  $ tar -vczf new-archive.tar.gz ./files-i-want-to-archive

  $ # Consider the full format instead, maybe we should actually use the full parameters more often?
  $ tar --verbose --create --gzip --file=new-archive.tar.gz ./files-i-want-to-archive
In short, using tar does not inspire joy and it feels overcomplicated, no matter how you look at it, possibly either because creating archives is a complicated domain (though the zip tool might not necessarily support that claim), or because the tool has grown over time and no longer does just one thing and does it well.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact