Build a CLI that starts instantly, composes with pipes, and never surprises a script.
Recommended stack
- Go + Cobra - language & framework. Single static binary, instant startup, trivially cross-compiled - the CLI gold standard.
- goreleaser - distribution. Tagged release → binaries, Homebrew tap, and checksums automatically.
- Node + commander - alternative. Right choice when your users already live in npm and need your JS ecosystem.
Build steps
- Design the grammar first:
tool <noun> <verb>with global--json,--quiet,--helpeverywhere. - Exit codes are the API: 0 success, 1 expected failure, 2 misuse; print errors to stderr only.
- Auto-detect TTY: human tables when interactive, plain/JSON when piped.
- Respect XDG config paths and an env-var override for every flag.
- Add shell completions and a
--versionthat CI can pin.
Watch out for
- Interactive prompts that hang scripts - every prompt needs a flag equivalent.
- Breaking output formats minor-version to minor-version; scripts parse you.
- Spinners and colors leaking into piped output.
Definition of done
tool --json | jqworks for every read command- Cold start < 50ms
- Install is one command (brew/npm/curl) with completions included