<br>
# [cli-best-practices](https://hackmd.io/@arturtamborski/cli-best-practices)
Collection of best practices for CLI tools 👩💻
:black_small_square:
[view on github.com](https://github.com/arturtamborski/cli-best-practices)
:black_small_square:
[view on hackmd.io](https://hackmd.io/@arturtamborski/cli-best-practices)
:black_small_square:
<br>
# 1. BASICS
This section points out a minimal set of necessary requirements to meet in order to make the CLI friendly and usable. Think `cargo` instead of `gcc`, or `httpie` instead of `curl`.
<br>
## 1.1. Make sure that your tool works
That may sound obvious but it's very important that your tool works. It prints something to stdout, it does not trip on it's own flags and that these flags actually are documented and have an effect on the application.
This point is intentionally obvious, because it's 'obvious', so you might not focus on it a lot and just assume you meet it, but to your user - it's the first and most important rule one sees when interacting with the tool.
Imagine how terrible it would be if you'd run a fresh'n'cool CLI tool only to see it breaking imidiately with some stack trace or even worse - obscure, undocumented error.
:sparkles: friendly tip :sparkles:
Consider adding at least some simple test scenario to your CI release pipeline, just to be sure that the CLI at least works. Might not be fully functional, but it has to at least work.
### Example
Bad
```shell
$ my-cool-tool
Stack trace: [...]
```
Good
```shell
$ my-cool-tool
Error: no input specified! Please use --help to find out more.
```
You should aim much higher than that, but this is sensible starting point for your tool.
<br>
## 1.2. Document all public flags
Every flag that's available to the user must be documented. It is discouraged to have hidden flags but if that's the case - make sure that hidden flags are not used as an example in documentation or do not show up in answers.
### Example
https://stackoverflow.com/a/24470998
https://stackoverflow.com/a/31092052
https://cgold.readthedocs.io/en/latest/glossary/-H.html
```shell
cmake -H # set home directory
cmake -B # set build directory
```
Both flags are undocumented, yet incredibly useful, so they are used and do appear
in various online answers and code examples.
<br>
## 1.3. Follow well established conventions
Don't reinvent the flags with your fancy syntax like `-Cool_Flag:someValue`. It's confusing for everyone, including you.
(Suggested by zapier.com)
### Example
Bad
```shell
$ 7z a ../lambda.zip -xr'!.venv' .
# to ignore directory you have to specify -x (exclude) and -r (recursive)
# and '!<directory>' -- to exclude it again?
# man 7z: Do not use "-r" because this flag does not do what you think.
```
Good
```shell
$ zip -r ../lambda.zip -x '.git' .
$ zip -r ../lambda.zip --exclude='.git' . # alternative version
# much cleaner :)
```
<br>
## 1.4. Use exit codes
Use, document, and inform the user about them.
### Example
```shell
$ ls /usrer/
ls: cannot access '/usrer/': No such file or directory
$ echo $?
2
$ man ls
[...]
Exit status:
0 if OK,
1 if minor problems (e.g., cannot access subdirectory),
2 if serious trouble (e.g., cannot access command-line argument).
```
<br>
## 1.5. Always provide `-h/--help` flag
Your tool has to provide some built-in way of informing the user what it does and how to use it. That's the minimum requirement but it's absolutely necessary. Man pages aren't always installed.
### Example
Bad
```shell
$ 411toppm -h
option `--height' requires an argument
$ 411toppm --help
411toppm: Use 'man 411toppm' for help.
```
Good
```shell
$ xz -h # or xz --help, it's the same output
Usage: xz [OPTION]... [FILE]...
Compress or decompress FILEs in the .xz format.
-z, --compress force compression
-d, --decompress force decompression
[...]
```
<br>
## 1.6. Learn from others, copy the best
[`gh-cli`](https://cli.github.com/manual/index),
[`heroku cli`](https://devcenter.heroku.com/articles/heroku-cli-commands),
[`httpie`](https://httpie.io/docs/cli/examples),
[`cargo`](https://doc.rust-lang.org/cargo/commands/cargo.html)
<br>
## 1.7. Keep the tool name short, unique and easy to remember
If that's not possible consider supporting alternative binary name (`angular-cli` → `ng`).
This point is highly specific to your case, it might be that you're building a collection of related CLI tools, where it makes sense to name the binary in a namespaced manner, like `gnome-session` / `gnome-session-new-session` / `gnome-session-properties`...
<br>
## 1.8. Tool has to be predictable and idempotent
In other words, it should behave the same way no matter the time of execution or other external factors.
For expanded explanation, see:
- https://daniel.haxx.se/blog/2021/12/06/no-easter-eggs-in-curl/
- https://unix.stackexchange.com/questions/405783/why-does-man-print-gimme-gimme-gimme-at-0030
- https://unix.stackexchange.com/a/406169
- https://labs.detectify.com/2012/10/29/do-you-dare-to-show-your-php-easter-egg/
- https://arslan.io/2019/07/03/how-to-write-idempotent-bash-scripts/
<br>
## 1.9. Tool has to be task oriented
The idea here is to use the tool to fulfill some need (like a need to copy a file, copy a repository, or format a filesystem). Your CLI tool might be specific to the service you're offering (like a `gh-cli`) in which case there are many tasks you can achieve, but because `gh-cli` is task oriented, it's easy to execute desired action.
### Example
```shell
$ gh --help
Work seamlessly with GitHub from the command line.
USAGE
gh <command> <subcommand> [flags]
CORE COMMANDS
browse: Open the repository in the browser
codespace: Connect to and manage your codespaces
gist: Manage gists
issue: Manage issues
pr: Manage pull requests
release: Manage GitHub releases
repo: Create, clone, fork, and view repositories
ACTIONS COMMANDS
actions: Learn about working with GitHub actions
run: View details about workflow runs
workflow: View details about GitHub Actions workflows
ADDITIONAL COMMANDS
alias: Create command shortcuts
api: Make an authenticated GitHub API request
auth: Login, logout, and refresh your authentication
completion: Generate shell completion scripts
config: Manage configuration for gh
extension: Manage gh extensions
gpg-key: Manage GPG keys
help: Help about any command
secret: Manage GitHub secrets
ssh-key: Manage SSH keys
FLAGS
--help Show help for command
--version Show gh version
EXAMPLES
$ gh issue create
$ gh repo clone cli/cli
$ gh pr checkout 321
```
<br>
## 1.10. Tool has to be friendly people and scripts
This point is often missing, especially in older CLI tools which were designed for proficient CLI users but with little thought for use in automated scripts.
That's why tools like `cut`, `awk` and others are so often seen in bash scripts - it's the only way to convert human readable output to machine readable format used by other tools.
### Example
Bad
```shell
$ docker ps -a | grep alpine | cut -c 1-12
# get container IDs which are based on alpine image
```
This command is very fragile to output-specific behaviour - it can easily fail if the output changes and it also isn't exactly correct, because `grep alpine` will only find the obvious cases, but it will not show containers which are based on apline by inheritance.
Good
```shell
$ docker ps -a --filter=ancestor=alpine --format "{{ .ID }}"
# get container IDs which are based on alpine image
```
Docker CLI was specifically designed to be parseable by scripts without the impact on user experience (when output changed for some reason). It will work reliably every time and is more correct, because it will check the actual dependency on alpine image instead of just searching it in the output.
<br><br><br>
# 2. INPUT AND FLAGS
<br>
## 2.1. As always, Consistency is the Key
By far the most commonly used (and thus the *only good way)* of writting flags is with the dash-case. Do not use anything else. Yeah, java uses `-Xmx`, but it's their problem, not your option. Do not use anything else other than `--option-name`. Please.
<br>
## 2.2. This applies to environment variables as well
`GOOD_ENV_VAR_NAME` vs `BAD_envVarName`. Yes, they are case sensitive, no, you should not leverage that. Please stick with upper casing or `SCREAMING_SNAKE_CASE` if that convinces you more.
<br>
## 2.3. Every flag that can have a default value, should have default value
Defaults have to be sensible and meaningful. If you are not sure, better to leave it off and come back after reasuring yourself on the most commonly used value.
<br>
## 2.4. Provide long options first, then shorten the most commonly used ones
<br>
## 2.5. Don't use `-longopt`, instead use `--longopt`
Copypaste of someones opinion:
I hate `-long` style options too. It's not just aesthetic/confusing, it's objectively worse/more limited:
- It means `-thing` != `-t -h -i -n -g`, so the latter has to be that long;
- You also can't have `readable-long-things-as-an-option`;
- You can't (or not without crazy logic and a confusing UX) have variables passed with no space, like `-ffoobar`
-
<br>
## 2.6. Named flags should be position independent
```jsx
ls --list --all .
# is the same as...
ls --all --list .
```
<br>
## 2.7. Long named flags > short named flags > positional arguments
```jsx
find -R . -name '*.txt' -type f
vs
find . -R -n '*.txt' -f
vs
find . r '*.txt' f
```
<br>
## 2.8. List of common conventions often used in CLI tools
- `-` for marking stdin / stdout
- `--` for marking end of arguments
- `--longopt=somevalue` for passing values with long flags
- `-s somevalue` for passing values with short flags
- `--pointer` / `--no-pointer` prefix flags with `no` for switch-like functionality
- `-f` / `--file` for selecting file
- `-o` / `--output` for specyfing output file
- `-i` / `--input` for specyfing input file
- `-h` / `--help` for displaying help
- `-n` for dry run
- `-v` / `--verbose` for increasing verbosity
- `-q` / `--quiet` for decreasing verbosity
- `-V` / `--version` for showing version
- [http://docopt.org/](http://docopt.org/)
Commonly established conventions take priority.
```jsx
-v -> verbose / version
-r -> reverse / recursive
-a -> all / append
-l -> list / lines
-n -> n things
```
`--recursive -> -r` / `--reverse -> -r`
`--all -> -a`
<br>
## 2.9. Allow passing sensitive values trough multiple channels
Flags are not always the best option, for example `--password=qwerty123` really won't work. There has to be a way to pass sensitive data without it being shown to the user.
A good rule of thumb is to expect your tool to be used in public CI pipeline or shown in youtube tutorial.
If you have to obfuscate the secrets in your docs to show the option then you're probably doing it incorrectly, because every user will have to keep that in mind as well.
`env MY_TOOL_SECRET | my-tool --password -` is a good way of sending the input secretly.
<br>
## 2.10. If your tool is big, split it into subcommands
```jsx
aws [global flags] acm [acm specific args] [acm specific flags]
aws [global flags] s3 [s3 specific args] [s3 specific flags]
aws --region=us-east-1 s3 ls --bucket=test
```
<br><br><br>
# 3. USER EXPERIENCE
<br>
## 3.1. Inform the user early
<br>
## 3.2. Don't go for a long period without output to the user
If you do print status messages like that, make sure to send them to STDERR if your utility outputs any actual data (like a report or file listing). Same for progress meters.
<br>
## 3.3. If a command has a side effect provide a dry-run/whatif/no_post option
<br>
## 3.4. For long running operations, allow the user to recover at a failure point if possible
<br>
## 3.5. Support scaffolding, if applicable
<br>
## 3.6. Support autocompletion
<br>
## 3.7. Make it easy to install and easy to update
<br>
## 3.8. Suggest commands on typing errors (`git satus` → `Did you mean git status`)
<br>
## 3.9. Always do the least surprising thing
`rm -rf /*` - rm has to be aware what might happen an try to block the user. It's too destructive to accept it without confirmation.
`pkill -v <some process id>` - `v` stands for "inverse input", so this actually kills every process except the one passed in input. Why?
<br>
## 3.10. Repair what you can — but when you must fail, fail noisily and as soon as possible
<br>
## 3.11. Design for the future, because it will be here sooner than you think
<br>
## 3.12. Aliases provide balance between brevity and discoverability
Consider splitting your cli into `Resources: ` as in what you can achieve and `Aliases` as in list of shortcuts to do stuff.
<br>
## 3.13. Piping is good for automation but people don't want to pipe
If its a common operation then provide a dedicated command for it. Either you will or every user will wrap some commands with subshells and pipes without setting ‘set -o pipefail”
<br>
## 3.14. Default to human-first output, but support multiple (json ftw)
Optimize output for humans, `10 days ago` > `2019-07-15T14:32:22Z`
You can use ISO for JSON :)
<br>
## 3.15. Avoid positional arguments where the order matters
which one is correct and why?
```jsx
emote add repo funk https://x.com/funk.json
vs
emote add repo https://x.com/funk.json funk
```
answer: neither! it's not obvious, you have to check docs to make sure = bad UX.
```jsx
emote add repo funk --url https://x.com/funk.json
```
command subcommand NAME —more-flags
<br>
## 3.16. Positional arguments are cool when the order doesn't matter
```jsx
emote repo delete funk more-funk
vs
emote repo delete more-funk funk
```
<br>
## 3.17. Expect that user will try to stop the tool at the worst moment (eg. during lengthy process like cloning a repo)
Be sure to not fail unexpectely and to not leave trash after unfinished action. You can overwrite action on CTRL+C to clean up right before the program finishes.
<br><br><br>
# 4. ACCESSIBILITY
<br>
## 4.1. Keep the output short
Try to not go over 80 characters per line of output
Bad
```bash
$ ls --help
[...]
--all -- this option allows you to print every file in given directory specified in the input so that you can see every file in local directory even if its hidden.
```
Good
```bash
$ ls --help
[...]
--all -- print every file including hidden ones
```
<br>
## 4.2. `$NO_COLOR` support
The CLI tool should support commonly used modifiers for it's output.
See [https://no-color.org/](https://no-color.org/) for more information.
<br>
## 4.3. `--verbose` support
Preferably with some way of increasing/decreasing it.
<br>
## 4.4. Localization support
Common pitfal: [http://www.scalingbits.com/aws/CLI](http://www.scalingbits.com/aws/CLI)
<br>
## 4.5. Translations
I don't actually know much about that and how it should work...
<br>
## 4.6. `$DEBUG` support
Tool should provide sensible way to debug it.
<br>
## 4.7. Don't use too many colors and don't expect users have more than 16 colors by default
CLI tools don't control their background so the ouput must be background-color agnostic. There shouldn't be any asumption as to the color scheme used by the user, instead all colors should be treated as hints only. Eample: use bold to visibly separate short important parts (like headers),
If in doubt, change the color to default and say what you have to say, most often than not this will end up looking better than some combination of inverse bold text with custom background color.
Unless your application controls the whole terminal (like vim, mutt, etc) it's just not worth playing too much with colors because you're raising your chances of readable output which should be your priority.
More info on colors: [https://accessibility.psu.edu/legibility/contrast/](https://accessibility.psu.edu/legibility/contrast/)
<br>
## 4.8. Decrease use of emojis to minimum and stick to the old and well known emojis
Think clearly on what you're trying to communicate, too many emojis may obfuscate this where a few words would do. Use emojis to transfer emotions and common behaviours (hand waving to say hello, confetti to express joy or sucess) instead of using them as pictograms with some underlying meaning. This meaning might not be valid in other cultures (praise hands is a good example) or it might not mean what you thought it means. If you have to stop and think what your output with emoji means then you should change that to clarify your intentions.
Another issue is with support - many fonts fonts don't support recent emojis or are visualised differently across operating system (consider every font used in every CI pipeline + every monospaced font on commonly used OS + some of these are not update (Ubuntu 12? Ubuntu 10? Old mac? They all will have problems with emojis) Use [emojipedia.com](http://emojipedia.com) to verify that emojis are actually representing the same thing and same meaning.
<br>
## 4.9. Use gender neutral language
[https://man7.org/linux/man-pages/man7/man-pages.7.html](https://man7.org/linux/man-pages/man7/man-pages.7.html)
<br>
## 4.10. Follow american spelling convention
color > colour, etc. It's just more common and causes less confusion, don't take this personally.
<br>
## 4.11. Support diagnostic flag of sorts
When your tool fails someone will try to report it,
(find example of issue where they ask to rerun the command with some diagnostic switch for easier debugging
<br>
## 4.12. Use short, terse and simple language for describing the program's behaviour
Do not use slang-specific words if they obfuscate otherwise simple meaning. Try to keep the program-specific slang or brand-specific slang to minimum. One example of that is `yargs` which uses pirate-like terminology to explain its meaning and create a brand around it. It's okay to do so as long as it doesn't spread accross the whole help page or documentation.
Simple things should be named simply, known things should be named with known words. Do not reinvent the common terms because you will not be understood by most of the world.
Example: some programs support plugins / addons / extensions and then there's ansible galaxy.
<br>
## 4.13. Be aware of your users, their backgrounds and your environment
Go commands often use `get` for downloading, so your tool should also use it if it needs that feature.
Try to discover, observe and mimic common behaviour of your ecosystem's tools.
<br>
## 4.14. Don’t use color as the only way to convey information
Users who are color blind cannot receive information that is conveyed only through color, such as in a color status indicator. Include other visual cues, preferably text, to ensure that information is accessible.
<br>
## 4.15. Avoid using blinking font (ANSI modifier)
Testing your work:
- assistive Tech
Familiraize yourself with screen-reader tech like VoiceOver JAWS and NVDA. If you can, get an assistive technology lab and test out your software. If you can't, improvise!
- Content and naming
Am I saying this in the simplest, most direct way? Do my error messages make sense and give the users a path forward?
- Yes, you too
Test with differently-abled users!
- Automation
Add automated a11y linter to your CI pipeline. Design automated tests with a11y in mind.
[https://speakerdeck.com/raqueldesigns/everyone-should-be-able-to-use-your-software-accessibility-for-balanced-teams?slide=42](https://speakerdeck.com/raqueldesigns/everyone-should-be-able-to-use-your-software-accessibility-for-balanced-teams?slide=42)
<br>
## 4.16. Give users enough time to complete an action
<br>
## 4.17. Explain what will happen after completing a service / command
<br>
## 4.18. Make important information clear
<br>
## 4.19. Let users check and change their answers before they submit them
Accessibility is important - make your applications accessible because its the right thing to do, but if you don't want to do it because it's the right thing to do, do it because it's the legally required thing - https://section508.gov/
<br><br><br>
# 5. DOCUMENTATION
At minimum your tool should be able to inform the user on how to use it effectively. This is most commonly done with `--help` flag.
<br>
## 5.1. Provide `--version` flag with understandable versioning scheme
If you're not sure which one to use go with semver, because it's the most popular one.[https://semver.org/](https://semver.org/)
<br>
## 5.2. Document every file that will impact the behaviour
If your app uses something from `/etc` document that. If your app reads `~/.config/<my-app>/config.ini` document that as well. Every file that impacts the CLI has to be documented, or at least mentioned.
<br>
## 5.3. Provide easy way of updating the tool by itself
`$ tool update` should be built in, tested and working out of the box. This might be as simple as downloading appropriate binary and replacing the original one, that's simple enough to support it without any dependencies. If your tool needs more things to update itself, consider simplifing it.
<br>
## 5.4. `--help` is not `man`
If possible, support `man` pages as well as your built-in documentation. Many CLI tools have extensive documentation built in under `help` subcommand which is useful, but also limited in some regards. The same docs can be easily regenerated to different format using tools like `ronn`.
[https://rtomayko.github.io/ronn/](https://rtomayko.github.io/ronn/)
[https://man7.org/linux/man-pages/man7/man-pages.7.html](https://man7.org/linux/man-pages/man7/man-pages.7.html)
<br>
## 5.5. Try to follow man pages when displaying help
```
NAMESYNOPSIS
CONFIGURATION [Normally only in Section 4]
DESCRIPTION
OPTIONS [Normally only in Sections 1, 8]
EXIT STATUS [Normally only in Sections 1, 8]
RETURN VALUE [Normally only in Sections 2, 3]
ERRORS [Typically only in Sections 2, 3]
ENVIRONMENT
FILES
VERSIONS [Normally only in Sections 2, 3]
ATTRIBUTES [Normally only in Sections 2, 3]
CONFORMING TO
NOTES
BUGS
EXAMPLES
AUTHORS [Discouraged]
REPORTING BUGS [Not used in man-pages]
COPYRIGHT [Not used in man-pages]
SEE ALSO
```
<br><br><br>
# 6. CONFIGURATION
<br>
## 6.1. Support XDG specification
[https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
[https://github.com/aws/aws-sdk/issues/30](https://github.com/aws/aws-sdk/issues/30)
this one drives me crazy, one of the most requested changes and it just hangs there for over three years now.
<br>
## 6.2. Don't invent your own config syntax for configuration
Please, don't. Every config format, even the most basic one like INI is better than your own.
<br>
## 6.3. Support multiple output formats if they are required by the users
It's better to do it correctly on your side than to let the users pipe your json to `yq` just because they need that output format badly for some reason.
You are creating this tool for humans, make it human friendly :)
<br>
## 6.4. If behaviour can be altered with env variables then it should be possible to do the same with flags
If your program uses `XDG_CONFIG_DIR` to place it's config, then you should also support `—config-dir` for doing the same thing.
<br><br><br>
# 7. STATEFULNESS
Keep it low. CLI is a simple state machine, and it should stay simple.
<br>
## 7.1. If your app has to store some state, store it in one, clearly indicated file.
Don't set environment variables to store anything, it just won't work.
The best way is to not have a state, second best is to keep it in one (and only one) file.
Good example: terraform
Bad example: ???
<br><br><br>
# 8. SCRIPTING
Always assume that your tool will be wrapped with some script. Folks from `apt-get` never expected that, but it's the reality.
<br>
## 8.1 Support JSON output
It's the most universal and by far the most popular way of outputing machine-readable data.
Bonus points: support filtering / limiting the output as well.
<br>
## 8.2. Be aware of signals
Expect that your tool will receive signals. Your tool should react most commonly to that.
(how, exaclty?)
<br>
## 8.3. Stderr is for errors, stdout is for output
Stick to it.
<br>
## 8.4. Expect input from stdin, pipe, unix socket, redirection from files etc
In other words: make it input-agnostic. Don't assume anything about the source input because it probably won't be true.
<br>
## 8.5. STDOUT is your API. You have to expect people wrapping it like an api
If you don't provide a machine-readable output (—json), then stdout will be used as a machine-readable output. It will be parsed, processed with various tools and will be relied upon to do the work. This matters *a lot* when your tool is big enough.
—json (or any other switch for machine readable output) is your safety valve, if you don't have that then you have to be way more concius on how output is used.
<br><br><br>
# 9. DEVELOPMENT
<br>
## 9.1. Test your code, heavly. Test your CLI commands heavly
One tip is to don't hook up annonymous function to your CLI framework but instead keep them in separate module and then hook them to framework's API explicitly in some separate module.
That way you will be able to easily test your command runners (functions) without the burden of dealing with CLI framework.
<br>
## 9.2. Connect your command handlers (functions) to the CLI framework boringly
It has to be simple. boring, moundaine and uninteresting code, because it is uninteresting code. The interesting part is in the command handler function, that's where magic happens! Keep the complexity out of the handler<>CLI framework bridge.
<br>
---
<br><br><br>
# 10. RESOURCES USED
<br>
## 10.1. Links
<br>
- [http://mds.is/a11y/](http://mds.is/a11y/)
- [https://www.w3.org/TR/WCAG21/](https://www.w3.org/TR/WCAG21/)
- [https://clig.dev/](https://clig.dev/#further-reading)
- [https://semver.org/](https://semver.org/)
- [https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46](https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46)
- [https://github.com/lirantal/nodejs-cli-apps-best-practices](https://github.com/lirantal/nodejs-cli-apps-best-practices)
- [https://www.shopify.com/partners/blog/content-strategy](https://www.shopify.com/partners/blog/content-strategy)
- [https://blog.developer.atlassian.com/10-design-principles-for-delightful-clis/](https://blog.developer.atlassian.com/10-design-principles-for-delightful-clis/)
- [https://devcenter.heroku.com/articles/cli-style-guide](https://devcenter.heroku.com/articles/cli-style-guide)
- [https://zapier.com/engineering/how-to-cli/](https://zapier.com/engineering/how-to-cli/)
- [https://eng.localytics.com/exploring-cli-best-practices/](https://eng.localytics.com/exploring-cli-best-practices/)
- [http://codyaray.com/2020/07/cli-design-best-practices](http://codyaray.com/2020/07/cli-design-best-practices)
- [https://dev.to/nickparsons/crafting-a-command-line-experience-that-developers-love-4451](https://dev.to/nickparsons/crafting-a-command-line-experience-that-developers-love-4451)
- [https://devcenter.heroku.com/articles/cli-style-guide](https://devcenter.heroku.com/articles/cli-style-guide)
- [https://relay.sh/blog/command-line-ux-in-2020/](https://relay.sh/blog/command-line-ux-in-2020/)
- [http://catb.org/esr/writings/taoup/html/ch01s06.html#id2877610](http://catb.org/esr/writings/taoup/html/ch01s06.html#id2877610)
- [https://scis.uohyd.ac.in/~apcs/itw/UNIXProgrammingEnvironment.pdf](https://scis.uohyd.ac.in/~apcs/itw/UNIXProgrammingEnvironment.pdf)
- [https://bitbucket.org/vasudevram/s](https://bitbucket.org/vasudevram/selpg/src/master/DevelopingALinuxCommandLineUtility.pdf)
- [https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html)[elpg/src/master/DevelopingALinuxCommandLineUtility.pdf](https://bitbucket.org/vasudevram/selpg/src/master/DevelopingALinuxCommandLineUtility.pdf)
- [https://man7.org/linux/man-pages/man7/man-pages.7.html](https://man7.org/linux/man-pages/man7/man-pages.7.html)
- [https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html](https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html)
- [https://www.youtube.com/watch?v=eMz0vni6PAw](https://www.youtube.com/watch?v=eMz0vni6PAw)
- [https://accessibility.psu.edu/legibility/contrast/](https://accessibility.psu.edu/legibility/contrast/)
- [https://accessibility.psu.edu/color/brightcolors/](https://accessibility.psu.edu/color/brightcolors/)
- [https://accessibility.voxmedia.com/](https://accessibility.voxmedia.com/)
- [https://accessibility.18f.gov/](https://accessibility.18f.gov/)
- [https://accessibility-handbook.mybluemix.net/design/a11y-handbook/](https://accessibility-handbook.mybluemix.net/design/a11y-handbook/)
- [https://ukhomeoffice.github.io/accessibility-posters/posters/accessibility-posters.pdf](https://ukhomeoffice.github.io/accessibility-posters/posters/accessibility-posters.pdf)
- [https://www.southampton.ac.uk/~km2/teaching/hci/lec19.htm](https://www.southampton.ac.uk/~km2/teaching/hci/lec19.htm)
- [https://speakerdeck.com/crystalprestonwatson/its-always-sunny-in-mobile-accessibility?slide=46](https://speakerdeck.com/crystalprestonwatson/its-always-sunny-in-mobile-accessibility?slide=46)
- [https://click.palletsprojects.com/en/7.x/](https://click.palletsprojects.com/en/7.x/)
- [https://rome.tools/#technical](https://rome.tools/#technical)
<br>
## 10.2. Collections
<br>
- [https://a11yresources.webflow.io/](https://a11yresources.webflow.io/)
- [https://github.com/brunopulis/awesome-a11y](https://github.com/brunopulis/awesome-a11y)
- [https://github.com/Kikobeats/awesome-cli](https://github.com/Kikobeats/awesome-cli)
- [https://github.com/alebcay/awesome-shell](https://github.com/alebcay/awesome-shell)
- [https://github.com/jlevy/the-art-of-command-line](https://github.com/jlevy/the-art-of-command-line)
- [https://stackoverflow.com/questions/28708037/recommendations-best-practices-on-custom-node-js-cli-tool-config-files-location](https://stackoverflow.com/questions/28708037/recommendations-best-practices-on-custom-node-js-cli-tool-config-files-location)
- [https://stackoverflow.com/a/28708450](https://stackoverflow.com/a/28708450)
- [https://oclif.io/docs/introduction.html](https://oclif.io/docs/introduction.html)
- [https://github.com/matuzo/HTMHell](https://github.com/matuzo/HTMHell)
- [https://github.com/palash25/best-practices-checklist](https://github.com/palash25/best-practices-checklist)
<br>
---
<br>
<small>
Thank you for reading :) You're welcome to comment this list on
hackmd or on github.
</small>
<br>
<small><details><summary><em>
my notes, please ignore for now :)
</em></summary><small></small>
- [x] TODO: ask `@carolynvs` for more resources, correct errors?
- [ ] TODO: find more people who care about good CLIs, ask for their help
- [x] TODO: share this on github (`awesome-cli-practices`? Still thinking on good title, probably something starting with `awesome-` so it's easier to find it among other GH repos).
- [ ] TODO: talk with a11y folks on their ideas for this specific environment (no resources so far...)
- [ ] TODO: clean it up, add example of bad/good to every point.
- [ ] TODO: add footnotes with references to source knowledge
</details></small>
<br>