「1つの仕事」の境界線。ソフトウェアのレイヤー構造
ソフトウェアを設計を始める時、あるいは main 関数をいきなり書き始めてしまう人もいるかも知れないが、どちらにせよそのソフトウェアにどのような機能を実装するのか考えるだろう。プログラミングで 1 番楽しい楽しい瞬間かも知れない。そしてこんな機能あったら便利だ、とか機能 A と B は共通ロジックを持てそうだ、などと考えている内にいつのまにか何でも出来るスーパーソフトウェアを生み出してしまう人が多いだろう。しかしそれはソフトウェア間の国境を塗りつぶし、崩壊寸前の巨大帝国を作り出すことに等しい。
大量の機能を持ったソフトウェアは複雑さを増し、機能間の連携は崩れ、バグを増やして崩壊していく。この巨大な帝国を崩壊させずに扱うことは難しいしとても疲れる作業になる。それよりは自らが管理可能な小さなソフトウェアを作りそれらを連携させていく方が現実的である。つまり、Unix 哲学である。詳細は省略するが Unix 哲学とは 1 つの仕事を上手に行い、それらを組み合わせることで複雑な処理を記述しようという考え方である。
ここで私は 1 つの疑問を持つ。1 つの仕事とはなんだろうかと。Unix 哲学の具体例としてはlsやgrepなどといったツールでありこれらはそれぞれファイル構成を明らかにする, 入力に対して検索を行うといった仕事に振り分けられる。しかしlsそのものも内部のソースコードは CLI 引数のパースから実際のファイルシステムとのやりとり、結果のフォーマットと表示など複数の機能から構成されているだろう。そしてそれらをまとめてlsと呼んでいるわけだが、例えば内部にlsやgrepを使用したシェルスクリプトを作ったらlsそのものも機能群の 1 つになるわけである。つまり 1 つの仕事というのは絶対的に決まるものではなく、相対的かつ階層(レイヤー)を分けて段階的に決定されるものであると言えるだろう。
シェルレベルのレイヤーから見ればlsは 1 つの仕事であると言えるが、それらの集合であるスクリプトのレイヤーから見ればlsは 1 つの仕事を達成するための 1 パーツに過ぎなくなる。
ここから言えるのは 1 つの仕事を見出すのは難しい上に、明確な正解はないということである。1 つの仕事の背景にはいくつもの 1 つの仕事があり、それは自己相似的な構造を成していると言える。
しかしそれでは自分はどのサイズ(どのレイヤーにおける 1 つの仕事と言い換えられる)でソフトウェアを作ればいいのか、どのサイズを超えたら別のソフトウェアにすればいいのか、そういう悩みが出てくるだろう。元も子もないが周りに合わせるのが 1 番である。grep や glow(cli markdown viewer)と組み合わせたいならそのレイヤーに合わせた機能に、web などで使うならそれらに合わせた機能にした方がいいだろう。ニュースリストを取得するのに 2 つも 3 つも api を使ってそれらを組み合わせる必要のあるサーバーは使い辛いだろう。しかしただ CLI のワンライナーで使いたいだけのツールにサブコマンドが 10 個もあったらそれは過剰だし、ソフトウェアとして分けた方が賢明に感じる。
例として CLI は基本的にはサブコマンドを実装しない方がいいと考えている。git などの多機能なコマンドはいいとしていわゆるユーティリティツールなどはサブコマンドに分けるくらいなら、別コマンドにするのが適切なレイヤーであろう。
結局のところソフトウェア開発においてどこに国境を引くか、機能を適切なくレイヤーを分け、取捨選択すること、言い換えれば機能への名付けとも言えるだろう、それらの思考が主たる業務であり、コーディングなんてものはあとの 10%の仕事に過ぎない。実際の所、実装に手間取ることはあるが、大概はどうにかなる。