Hesitation kills trust fast. The second users have to guess the command, decode the output, or wrestle with the error, the tool stops feeling powerful.
The best CLIs feel obvious. You know what to type, what happened, and what to do next.
Practical guideline for CLI design 🎯
1. Human-first design
Guideline:
- Design for the person typing the command, not just for scripts.
- Make command names understandable.
- Make help text readable by humans.
- Don’t expose internal implementation details by default.
Good:
deploy service --env staging- Help says:
Deploy a service to an environment.
Bad:
svcctl dp -e stg- Help says:
Executes deployment pipeline operation.
Reality check:
If users need tribal knowledge to use your CLI, the design is bad.
2. Simplicity and composability
Guideline:
- Make each command do one job well.
- Send real output to
stdout, diagnostics and errors tostderr. - Return proper exit codes:
0for success, non-zero for failure. - Support machine-readable output like JSON when useful.
- Allow piping and scripting without hacks.
Good:
tool list --jsontool list | grep failed
Bad:
- Mix status logs, tables, and errors into one unparseable output stream.
- Print success banners into
stdoutthat break scripts.
Reality check:
If your tool can’t be piped cleanly, it’s not a serious CLI.
3. Consistency
Guideline:
- Reuse standard flag names like
--help,--json,--verbose, and--quiet. - Use the same verbs everywhere:
start/stop,add/remove,enable/disable. - Keep naming, output style, and behavior consistent across subcommands.
- Follow a predictable command structure. Noun-noun-verb is a strong pattern for complex CLIs.
Good:
tool target addtool target removetool daemon starttool daemon stop
Bad:
tool add-targettool target-deletetool daemon launchtool daemon halt
Reality check:
Inconsistent CLIs force users to relearn the tool every time.
4. Clarity: say just enough
Guideline:
- Show enough output so users know the tool is working.
- Keep success output brief.
- Don’t dump walls of debug noise unless requested.
- Put the important part of the message where users will notice it.
Good:
Uploading artifact... doneFailed to connect to device. Check network access and retry.
Bad:
- No output for 15 seconds, then
Exception: transport error - Or 200 lines of stack trace for a common mistake.
Reality check:
Too little output feels broken. Too much output is just as bad.
5. Discoverability
Guideline:
- Support
-hand--helpeverywhere. - Put examples in help text early.
- Show common commands first.
- Suggest corrections for typos when confidence is high.
- Link to fuller docs from help output.
Good:
tool --help shows:
- what it does
- common commands
- examples
- key flags
Error:
Unknown command 'pss'. Did you mean 'ps'?
Bad:
- Help output is a raw flag dump with no examples.
- Error:
invalid subcommand
Reality check:
If users have to leave the terminal to figure out basic usage, your help is weak.
6. Conversation as the norm
Guideline:
- Assume users will learn by trial and error.
- Suggest next steps after successful commands.
- Confirm risky actions before execution.
- Offer dry-run mode for destructive or complex operations.
Good:
After tool plan --dry-run:
Run 'tool apply' to make these changes.
Before deletion:
Type the project name to confirm deletion:
Bad:
tool delete prod-db- Immediately deletes production data with no confirmation.
Reality check:
If your CLI punishes normal human mistakes, it’s hostile.
7. Robustness and responsiveness
Guideline:
- Validate input early.
- Print something quickly if work may take time; responsiveness matters.
- Show progress for long-running tasks.
- Use timeouts for network operations.
- Make reruns safe where possible.
Good:
Connecting to target...Downloading 3/10 layers...Manifest file not found: ./build/manifest.json
Bad:
- Hangs silently.
- Fails late after doing partial damage.
- Requires manual cleanup after interruption.
Reality check:
A tool that feels flaky is flaky, even if the code usually works.
8. Error messages that actually help
Guideline:
- State what failed, why, and how to fix it.
- Include the offending value when useful.
- Use plain language, not internal jargon.
- Provide a doc link or bug-report path for hard failures.
Good:
Failed to save config: ./config.yaml is read-only. Run 'chmod +w ./config.yaml' and try again.manifest or product_bundle must be specified
Bad:
InvalidArgsNotFoundGeneral failure
Reality check:
If your error message needs another error message to explain it, you failed.
9. Accessibility
Guideline:
- Never rely only on color to convey meaning.
- Support
--no-colorand respectNO_COLOR. - Use plain language.
- Avoid ASCII art-heavy formatting; it hurts readability and screen reader support.
Good:
ERROR: build failed- Optional red color, but meaning is still clear without color.
Bad:
- Only color the line red with no label.
- Use decorative ASCII boxes for core output.
Reality check:
If your output is unreadable in a screen reader or CI log, it’s poorly designed.
10. Safe interactivity
Guideline:
- Only prompt when running interactively.
- Always provide non-interactive alternatives via flags or args.
- Never require prompts for automation.
- Don’t ask unexpected questions in non-interactive tools.
Good:
- Interactive:
Enter API token: - Scriptable:
tool login --token-file token.txt
Bad:
- CI job hangs because the CLI suddenly asks:
Continue? [y/N]
Reality check:
A CLI that blocks automation is a liability.
11. Future-proofing
Guideline:
- Treat your CLI as a long-term contract.
- Prefer additive changes over breaking ones.
- Deprecate before removing.
- Don’t create shortcuts that block future growth, like ambiguous abbreviations.
- Avoid catch-all command behavior that makes expansion risky.
Good:
- Add
--format jsoninstead of changing default output. - Warn:
--old-flag is deprecated and will be removed in v3. Use --new-flag.
Bad:
- Change the meaning of an existing flag.
- Let
tool imeaninstall, then later regret it when you needinit.
Reality check:
If people script your CLI, breaking changes are your fault, not theirs.
12. Naming and structure
Guideline:
- Use clear, lowercase names.
- Avoid cryptic abbreviations.
- Keep names short, but not vague.
- Group related commands under one tool instead of spawning random standalone commands.
Good:
ffx component runtool package build
Bad:
run-componentpkgbldbtwhen it could mean five different things.
Reality check:
Clever names age badly. Clear names scale.
Quick CLI design checklist
- 🎯 Is the command understandable without insider knowledge?
- 🧩 Does it work cleanly in scripts, pipes, and automation?
- 🔁 Are naming, flags, and verbs consistent across commands?
- 🗣️ Does output say enough without becoming noise?
- 🔍 Does
--helpactually help? - 🤝 Does the CLI guide users instead of punishing them?
- ⚡ Does it feel responsive within the first moment of execution?
- 🛠️ Are errors actionable and human-readable?
- ♿ Is it accessible without color or fancy formatting?
- 🤖 Can it run fully non-interactively?
- 🧱 Will today’s design still hold up after future expansion?
- 📚 Are docs, examples, and support links easy to find?