title: "Notes on writing a CLI people will actually use"
date: 2025-01-09
tags: [craft]
reading_time: 8 min
slug: notes-on-writing-a-cli-people-will-actually-use
---
Notes on writing a CLI people will actually use
Most CLIs fail at being CLIs the moment they start acting like applications. A good CLI is a function you can call from a terminal; a bad one is an app you have to learn.
Two years of building internal tools, a pile of feedback from people who'd rather not be using my stuff, here's what actually moves the needle.
Defaults are the product
The default command is 80% of the user experience. If typing mytool without arguments doesn't do something useful — print help, run the common case, explain itself — the rest of the polish doesn't matter.
Whatever the most common subcommand is, make it the default. Don't hide behind subcommands for their own sake.
Flags should be rare
Every flag is a decision I'm asking the user to make. Users don't want to make decisions; they want their thing to work.
"The best flag is the one that doesn't need to exist."
If two flags are almost always set together, collapse them. If a flag has a sensible default 95% of the time, set that default and let the rare case override.
Errors are documentation
When something fails, the error message is the only documentation most users will read. Write it like a human:
✗ Cannot deploy — your git working tree has uncommitted changes.
Run `git status` to see them, commit, then retry.
Not:
Error: WORKING_TREE_DIRTY
The three commands most CLIs forget
mytool --versionmytool --help(andmytool subcommand --help)mytool doctor— or whatever you call "is my environment OK?"
The first two are table stakes. The third is the one that separates polite CLIs from great ones.
One last thing
Ship with one good example in the README. Not three. Not a reference. One, annotated line-by-line. That's the example people will copy, modify, and base their mental model on.