コマンドラインインターフェース ガイドライン
(clig.dev)伝統的な Unix の原則に従いつつ、現代向けにアップデートしたオープンソースガイド
-
CLI デザイン哲学
→ 人間優先
→ 一緒に動くシンプルな部品
→ プログラム間の一貫性を保つ
→ 必要な分だけ話す(少なすぎず多すぎない出力)
→ 見つけやすくする(包括的なヘルプ、例、次に実行すべきコマンドの提案、エラー時に何をすべきかの提案)
→ 普通の会話のように
→ 堅牢に
→ ユーザーに共感する
→ 混乱: ルールを破る必要があるなら、意図と目的を明確にすること
-
CLI ガイドライン
→ 基礎
✓ コマンドライン解析ライブラリを使うこと: Go(Cobra,cli), Node(oclif), Python (Click,Typer), Ruby(TTY)
✓ 成功時は 0、エラー時は 0 以外のコードを返す
✓ 出力は stdout
✓ ログ、エラーなどのメッセージは stderr
→ ヘルプ
✓ オプションを指定せずに実行したらヘルプを表示 (-h, --help)
✓ デフォルトでは簡潔なヘルプを表示
· このプログラムが何をするのか · 1〜2 個の呼び出し例 · フラグの説明(多くなければ) · 追加説明用の --help✓ -h, --help オプション時は完全なヘルプを表示
✓ フィードバックや issue を受け付けるための経路を提供
✓ ヘルプでは Web 版ドキュメントへのリンクを提供
✓ 例で説明すること
✓ 例が多いなら、どこか別の場所に置いておくこと(チートシートまたは Web ページ)
✓ man ページは気にしなくてよい(あまり使われず、Windows でも動かない)
✓ ヘルプが長いなら pager にパイプすること
✓ 最もよく使うフラグとコマンドをヘルプの先頭に表示
✓ ヘルプでフォーマットを使うこと(太字)
✓ ユーザーが何かを間違え、その内容を推測できるなら提案すること
✓ あなたのコマンドが何かを pipe で受け取ることを想定しているのに、stdin がインタラクティブターミナルなら、ヘルプを表示してすぐ終了すること
→ 出力
✓ Human-readable(人が読める)出力が最も重要
✓ ユーザビリティに問題がないなら machine-readable 出力を提供
✓ human-readable のために machine-readable が不可能になるなら --plain オプションで grep / awk などと連携できるように
✓ --json 入力を受けたら JSON フォーマットで出力
✓ 成功時は出力がないほうが良いが、必要なら簡潔にすること。-q オプションで不要な出力を除外できるようにする
✓ 状態を変更したなら、ユーザーに伝えること(git push の出力を参照)
✓ 現在のシステムの状態を見やすくすること
✓ ユーザーが実行できるコマンドを提案すること(git status を打つと git add / restore を表示するように)
✓ プログラムの内部を超える動作は明示的であるべき。ユーザーが指示していないファイルを読み書きすること(キャッシュ)や、リモートサーバーに接続すること(ファイルダウンロード)など
✓ ASCII アートを使って情報密度を高める
✓ 意図を持って色を使うこと。乱用しないこと
✓ ターミナルでない場合、またはユーザーが要求した場合は色を無効にすること
✓ stdout がインタラクティブターミナルでなければアニメーションを表示しないこと
✓ 何かを明確にするときにだけシンボル/絵文字を使うこと
✓ デフォルトでは、作者にしか分からない情報は出力しないこと
✓ stderr をログファイルのように使わないこと(少なくともデフォルトではそうしないこと。詳細モードでのみ ERR、WARN のようなログレベルを出力すること)
✓ 大量のテキストを出力するなら less のようなページャーツールを使うこと
→ Error
✓ エラーを Catch し、人間向けに文言を書き直すこと
✓ Signal-to-noise ratio(信号対雑音比)が重要。同じエラーが何度も出るなら、説明するヘッダーとともにまとめて出力する
✓ ユーザーが最初に目にする場所がどこかを考慮すること
✓ 予期しない/説明できないエラーが出たら、debug/trace 情報を提供し、このバグを開発者に送る方法を説明すること
✓ バグレポートを追加の手間なく送れるようにすること。(必要な情報をすべて含んだ URL を作り、ブラウザに入れるだけで報告が完了するように)
→ Argument & Flags : 引数とフラグ
✓ 引数: 位置パラメータ。順序が重要。cp bar foo と cp foo bar は異なる
✓ フラグ: 名前付きパラメータ。「-r」のような 1 文字、または「--recursive」のような複数文字。順序は通常重要ではない。
ユーザー値を含むこともある。--file foo.txt または --file=foo.txt✓ 引数よりフラグを優先すること。より多くのタイピングが必要だが、より明確。引数が多いと後から機能拡張するときに難しくなる
✓ フラグは短い版と完全版の両方を持つこと。スクリプトで完全版を使えば追加説明が不要
✓ 主に使うフラグにだけ 1 文字フラグを使う
✓ 簡単な動作のために複数の引数を受け取ることも可能
✓ 異なる 2 つ以上の引数が必要なら、何かを間違っているのかもしれない
✓ フラグは(既にあるなら)標準的な名前を使うこと
-a --all , -d --debug , -f --force , -h --help , -o --output , -p --port , -q --quiet , -u --user✓ ほとんどのユーザーに適したものをデフォルトにすること
✓ ユーザーが入力が必要な引数/フラグを指定したのに値がなければ、ユーザーに入力を求めること
✓ 常に引数/フラグで値を渡す方法を提供し、必ず入力(prompt)を要求しないこと
✓ 危険なことをする前には必ず確認を求めること
✓ 入力または出力がファイルなら、- で stdin から受け取るか stdout に出力することをサポートする
$ curl https://example.com/something.tar.gz | tar xvf -✓ フラグが追加の値を受け取れるなら、「none」のような特別な単語を許可すること。ssh -F none
✓ 可能なら引数、フラグ、サブコマンドは順序に依存しないようにすること
✓ 機密性の高い(パスワードのような)引数値はファイルから入力できるようにすること
→ Interactivity
✓ stdin がインタラクティブターミナルのときだけ prompt やインタラクティブ機能を使うこと
✓ --no-input が渡されたら、prompt やいかなるインタラクティブ機能も使わないこと
✓ パスワードを入力してもらうときは、ユーザーの入力値を表示しないこと
✓ ユーザーが簡単に抜けられるようにすること(vim のようにしないこと)。Ctrl-C が動作するようにすること。ssh、tmux などプログラム実行関連で Ctrl-C が使えないなら、SSH のように ~ で始まる escape sequence を提供することを明確に表示すること
→ Subcommands
✓ 複雑なツールは subcommand を提供して複雑さを減らせる
✓ また、密接に関連した複数のツールがあるなら、これらを 1 つのコマンドにまとめて便利に使えるようにできる
✓ サブコマンド間で一貫していること。同じフラグは同じ意味で、似た出力フォーマットを持たせる
✓ 複数段階のサブコマンド間で一貫した名前を使うこと
✓ 紛らわしい、または似た名前のコマンドを入れないこと。update と upgrade のようなもの
→ Robustness
✓ すべてのユーザー入力を検証すること。早めにチェックし、理解できるエラーを表示すること
✓ 速さより応答性のほうが重要
✓ 長くかかるなら進捗を見せること
✓ 可能なら並列に処理すること。ただしよく考えて行うこと。
✓ タイムアウトを入れること
✓ 冪等(idempotent)にすること。(再実行しても結果が変わらないこと)。エラーが出たときにシェルで上矢印を押して、前の続きとして再実行できるようにする
✓ Crash-only にすること。Idempotence の次の段階。作業後にクリーンアップを行う必要がない、または次回実行までクリーンアップを遅らせられるなら、プログラムは失敗または中断時に即座に終了できる。
✓ 人はあなたのプログラムを誤用するものだ
→ Future-proofing
✓ 可能なら変更は追加(additive)方式で。既存機能を変えて互換性を壊すのではなく、新しいフラグを追加すること
✓ 追加方式の変更でないなら、まず警告すること
✓ 人向けの出力変更はたいてい OK
✓ よく使われるサブコマンドがあるからといって、明示しなくてもそれを実行する catch-all サブコマンドは作らないこと
✓ 任意のサブコマンド略称を許可しないこと
✓ いつか動かなくなる「時限爆弾」は作らないこと
→ Signals and control Characters
✓ ユーザーが Ctrl-C(INT signal)を入力したら、できるだけ早く中断すること
✓ ユーザーが長くかかるクリーンアップ中に Ctrl-C を押したなら無視し、再度押せば強制終了できるようにすること
^CGracefully stopping... (press Ctrl+C again to force)→ Configuration
✓ XDG(X Desktop Group) 仕様に従うこと
✓ あなたのプログラムのものではない設定を変更するなら、ユーザーに確認を取り、何をするのかを明確に伝えること
✓ 設定パラメータは優先順位に従って適用すること
フラグ > シェル環境変数 > プロジェクトレベル設定 (.env) > ユーザー設定 > システム設定→ Environment Variables
✓ 環境変数は、コマンドが実行されるコンテキストによって変わる動作のためのもの
✓ 移植性を最大化するため、環境変数は大文字/数字/アンダースコアのみを含み、数字で始まってはならない
✓ 可能なら環境変数には 1 行の値(single-line)を使う
✓ 広く使われている名前は使わないこと
✓ 可能なら汎用環境変数を確認して使う
NO_COLOR, DEBUG, EDITOR, HTTP_PROXY, SHELL, TERM, TERMINFO, HOME, TMPDIR, PAGER, LINES ..✓ 必要なら .env から環境変数をロードする
✓ 設定ファイルに .env 拡張子は使わないこと
→ Naming
✓ 名前はシンプルで覚えやすい単語にする
✓ 小文字のみを使い、必要なときだけ -(ダッシュ)を使う
✓ 可能なら短く
✓ キーボードで入力しやすく
→ Distribution
✓ 可能なら単一バイナリで配布
✓ 簡単にアンインストールできるように
→ Analytics
✓ ツールの利用状況やクラッシュデータを、ユーザーの同意なくあなたに送信しないこと
3件のコメント
良い内容をありがとうございます。
Rust や Go のおかげで、単一バイナリとしてうまく作られた優れたコマンドラインツールがますます増えている気がします。
最近便利なコマンドラインツールまとめ https://ja.news.hada.io/topic?id=793
生産性を高めてくれる Rust command line ユーティリティ https://ja.news.hada.io/topic?id=2958
作ることも、さらに手軽かつ強力になってきています。
Rust で Command Line アプリを作る https://ja.news.hada.io/topic?id=972
Caporal.js - Node CLI 開発向けフルフレームワーク https://ja.news.hada.io/topic?id=2378
create-node-cli - Node.js で CLI を簡単に作る https://ja.news.hada.io/topic?id=3268
Gooey であらゆる言語および CLI ツールの GUI を作る https://ja.news.hada.io/topic?id=582
ink - React で CLI を作る https://ja.news.hada.io/topic?id=2041
簡単に訳しながら、私自身も多くを学びました。終わってから見ると、リポジトリ自体を翻訳したほうがよかったのではないかとも思います。^^;;