CLIs are stable and versatile, thus enabling creativity.
Your software will become a part in a larger system. Your only choice is over whether it will be a well-behaved part.
– CLIg.dev
Philosophy
- Design for humans, because usage by other programs is less common nowadays.
- Keep following UNIX conventions: stdin/out, exit codes, plain lines for text output, JSON when structure is needed.
- Consistency with other tools, because efficiency comes from transferrable experience.
- Just enough output to avoid impatience.
- Help users discover help texts, examples, etc.
- CLI usage often resembles a conversation. The tool should therefore suggest corrections with errors, clarify progress in multi-step processes, etc.
- Robustness by graceful failure, idempotency, explaining problems, rather than barfing stacktraces, and generally: simplicity.
- Being a kind, emphatic helper.
- Abandon standards when harm is demonstrated.
Guidelines
- Use an argument parser.
Return numeric exit codes, with 0 for success.
Send output to
stdout
to support|
ing. Send log and error messages tostderr
to not pipe those. - Help texts for: no option,
-h
or--help
. Help links back to project repo or website. Put example into help texts. Structure help texts into a logical order and with formatting. Suggest corrections in error messages. Don’t wait silently forsdtin
(likecat
does). - Document what a tool is for and not for separately from help texts, in terminal and on the web.
- Output: Prioritise its human- over machine-readability,
if necessary with
--plain
/--json
flags. Report success only briefly and offer-q
uiet option. Report state changes to the user and offer astatus
subcommand that suggests next steps. Use ASCII art, color, emojis, etc. where appropriate. Show log levels or debug info only in a-v
erbose mode. Useless -FIRX
or other pager for outputting more than 1 page of text. - Errors should be caught internally and rewritten for humans. Increase signal-to-noise ratio with grouping, and putting most relevant guidance to the end. Support bug reporting by writing stacktraces to logfiles, and link to a form/template.
- Args & flags: Prefer (named) flags to (positional) arguments. Use 1-letter flags sparingly and stick to existing terms.
- Subcommands: help reduce complexity, when they enable discovery of related tools and use consistent nounds, verbs, flags, etc.
- Robustly handle user input: early, with understandable errors. Respond within 100ms and before longer operations. Network operations should have timeouts. Parallelism is useful, but makes output 10x harder.
- Interface changes should be additive, rather than breaking existing behaviour. Follow SemVer conventions to announce unavoidable breaking changes. Don’t allow abbreviations or catch-all in the first place.
- Respect signals like
Ctrl+c
and inform user about ongoing cleanup operations. - Configuration options should be informed by specificity, stability and complexity. Varying for each invocation: flags. Stable within a project but not for each user: flags & env vars. Stable for all users within a project: config file, according to XDG. Never append to system-wide config file, unless you also date & comment your addition. Use this order also for precedence: flags to sys-wide.