home / tools / the shape of a good cli

The shape of a good CLI

Most CLIs are accidents. They evolve from a script someone wrote on a Tuesday. The good ones share a small set of choices made early: about defaults, verbs, and how silence should feel.

I’ve been thinking about this for a while. Every time I open a project I haven’t touched in a year, the same small frictions appear, and they’re almost never the things I optimized for at the time. There’s a pattern in what survives a year of neglect, and a pattern in what doesn’t.

01 · Silence as a default

The first thing I do is delete. Not refactor, delete. The shape of the project becomes much more visible once you remove the parts you’re no longer sure about.

# distill: strip a markdown file down to its headings.
distill < notes.md

02 · Verbs first

The second thing I do is rename. Variables, files, functions.

The best refactor is a rename. The second best is a delete. Everything else is a guess.

03 · Output is data

When you treat your tool’s output as data, two things become true. The first is that you stop printing things that aren’t.

  • Default to one record per line.
  • Default to silence when nothing happened.
  • Reserve stderr for the human; stdout for the machine.

04 · On exits and errors

Exit codes are the part of the API everyone forgets. Be quiet by default, and explicit when asked.


thanks for reading. the rest of the archive is here.