単一バイナリ配布の CLI に Go を選ぶ理由
一言まとめ
`go build` が静的リンクの単一バイナリを全プラットフォームに吐き、goreleaser で Homebrew / Scoop / APT まで一括配布できる。標準 `regexp` は RE2 で線形時間が保証される。Rust は libssl / libc の動的リンクで配布事故を起こした前例があり、Zig は 2026 年時点でエコシステムが若い。
目次 6 sections
調査時点: 2026-04。公開 OSS の mask-pipe(標準出力の秘密情報をリアルタイムでマスクする CLI。github.com/c12o-dev/mask-pipe)の ADR から。
要件
- macOS / Linux / Windows × amd64 / arm64 で動く、ランタイム依存ゼロの単一バイナリ
- ホットパスは stdin に対する正規表現マッチ(ストリーミング)
- 秘密情報を扱うので、メモリ安全であること
Go を選ぶ理由
| 観点 | 内容 |
|---|---|
| 配布 | go build が静的リンクの単一バイナリを生成。goreleaser がクロスコンパイルと Homebrew / Scoop / APT のアセット公開を 1 コマンドで行う |
| 正規表現 | 標準 regexp は RE2 ベースで O(n) の線形時間が保証される。ストリーミングの遅延を予測可能にする |
| エコシステム | PTY ライブラリ(creack/pty)など、シェル統合の拡張に要るものが揃っている |
| 貢献者 | CLI ツール作者に馴染みが深く、貢献者の母数が大きい |
トレードオフ
- バイナリサイズは 5〜8MB(未 strip)。Rust の積極的最適化なら 2MB 程度。必要なら UPX で圧縮する。
- Go の
regexpは複雑なパターンでは Rust のregexクレートより遅い。CPU バウンドでない用途なら許容し、ベンチマークで問題が出てから再評価する。
採らなかった選択肢
- Rust: 性能と
regexクレートは一級だが、クロスコンパイルで OpenSSL / libc のリンク問題を踏みやすい。Teller(Rust 製の秘密情報ツール)がlibssl.so.1.1の動的リンクで Ubuntu 22.04 以降に起動しなくなった前例がある。ビルド時間も長く、小規模チームの反復を遅くする。 - Zig: 小さな静的バイナリを作れるが、本番システムで動かすツールにはエコシステムが若い。
- Python / Node.js: ランタイムが要る時点で「入れて忘れる」体験が成立しない。
- C: 秘密情報を扱うツールでメモリ安全性を手放す理由がない。
関連
更新履歴
- 2026-04-16: 初版(mask-pipe ADR-0001)
- 2026-10-17: Wiki へ移植、概念ページとして再構成