Seven principles for building a CLI that works well — for the data engineer running it in a terminal, and for the agent running it in a pipeline.
01
A CLI isn't just used by a person at a terminal anymore. It's also run by scripts, CI jobs, and other tools. The same commands, flags, and outputs should work in both cases. If something only works interactively or only makes sense to a human, it becomes harder to test and automate. Designing for both use cases usually improves the tool overall.
02
If a command needs input, that input should be passable as a flag. Prompts can still exist, but they shouldn't be required. In practice, there are three ways to provide values: flags, environment variables, or prompts. Once the value is provided, execution should be the same.
From today's session
The configure command has no --id or --token flag. The only path is interactive. When the paste failed, the secret ran as a shell command and became visible in terminal history.
What it could look like
03
The top-level commands should show what the tool manages. Use nouns at the top level and verbs for actions beneath them. Mixing everything together or adding commands without structure makes the CLI harder to understand as it grows.
From today's session
Running montecarlo incidents --help returns "No such command." Alerts exist only as a bulk export. The most important things MC does are not queryable from the CLI.
What it could look like
04
Output should be readable and structured. That usually means separating data from presentation internally, so commands can return structured data and format it as needed. This makes it easier to support formats like JSON without extra work later.
From today's session
Rows wrap mid-row at normal terminal widths. There is no status column. The truncation message doesn't mention --limit. There is no --json flag anywhere in the CLI.
What it could look like
05
Error messages should explain what went wrong and what to do next. When possible, point to the exact command or fix. Use distinct exit codes so scripts can tell different failure cases apart.
From today's session
The first error tells you to run configure but not what configure needs or where to get a key. The second exposes an internal GraphQL URL. Both exit with code 1 — the same code used for every other failure.
What it could look like
06
Users should be able to see how the tool is configured and what it's doing. Things like credential sources, active accounts, and config precedence shouldn't be hidden or require digging through files or source code.
07
Commands, flags, and concepts should be clearly explained in the built-in help. A user should be able to understand how to use the tool without leaving the terminal. This also helps automated systems that rely on help text to figure out how the CLI works.
From today's session
Every description in the monitors group trails off mid-sentence. "mac" means monitors-as-code, not macOS — you would not know that from the help text.
What it could look like