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.