2014/12/31

設定ファイルが難しすぎるシステムを作ってしまうのも過剰な抽象化の一種

設定ファイルが下手に高機能すぎて、それに頼りすぎた結果、運用環境が非常に難解になってしまうことがある。そういう設定ファイル地獄のようなシステムができてしまう理由は、開発しているプログラマのレベルが低いからというのではなく(それもないわけではないと思うが)、いくつか他の構造的な理由があると思う。

  1. 柔軟性が無条件に良いものだと思っているから ―― コードを変更せずに設定ファイルでカスタマイズできるならそっちのほうがいい設計に決まってるよ、という思い込みのある人が多い。実際には、ベタに書けば簡単なものを設定に出すと複雑になることが多いから、これは事実ではないのだけど。
  2. バイナリのアップデートが面倒だから ―― 運用ポリシーによっては、バイナリの更新はオオゴトだけど設定変更は通常作業みたいな環境がある。そういう環境では、設定にできるだけ多くのものを追い出してインストール後の柔軟性を最大化しようというインセンティブが生じる。さらに、そういうポリシーのもとで育ったエンジニアはそれが常識だと思って内的なベストプラクティスにしてしまっているので、そうではない環境でもついそれを適用してしまう。
  3. 汎用のサーバプログラムの設定ファイルも難解だから ―― 汎用のサーバプログラムの設定は、オライリーから分厚い本がでるほど難しいものがよくある。ああいうプログラムは様々な環境で使われるから柔軟性を確保するために設定ファイルとプログラムとがああいうふうに分離されているわけだが、自分で自分のためのサーバプログラムを書く場合はあれはお手本にならない。
  4. 他愛もないものが徐々に恐ろしく複雑なのものに成長してしまうから ―― 大抵「この場合だけこの設定を適用したい」みたいなニーズを満たすために条件分岐のようなものを足すところから複雑化が始まる。条件として書ける式のパターンをもっと汎用的にしたりして、ループや変数もどきをアドホックに足していくと、結果としてひどい出来のスクリプト言語みたいなものが出来てしまうことがある。そういう謎言語(当事者にはプログラミング言語として認識されていない場合も多い)を書くのが辛すぎて設定ファイルを自動生成しようなどという話が出てきたらもう末期だ。関係者全員が次第に複雑化するシステムに徐々に慣らされてしまうので、そういうインクリメンタルな「改善」をどこかの時点で拒否するのは難しい。
  5. プログラマのモラルハザード ―― 設定は運用任せという体制の場合、プログラマはできる限り設定ファイルに機能を移したほうが仕事が簡単になる。なんでも「原理的には設定ファイルで可能」という状態にしておけば、設定ファイルの難解さから生じる問題も含めてすべて運用の責任ということにできる。運用チームの地位が低い場合には特にこれは問題になる。
設定ファイル問題への究極の解はないと思う。しかし前回書いたように、具体的なユースケースがわかっているのに不要な一般化を行わないほうがいいという話と同じで、やるべきことは大抵の場合コードで素直に直接実装するのがよいと思う。「こんな環境依存の具体的なことはコードで直接書くのではなくて設定ファイルでなんとかならないかな?」という誘惑を感じることは多いが、そういう誘惑はほとんどの場合無用にシステムを複雑化することになるだけだろう。ベタに書いてしまうくらいが正しいバランスだったりすることが多いと思う。


2014/12/29

すべてのソースコードが手元にあるのに不要な抽象化を行うのはよくない

「よい」とされているプログラミング手法のひとつに差分プログラミングがある。クラスを継承して親クラスとの差分だけのコードを書けば、親ですでに実装されている機能はそのまま使えて、かつカスタマイズもできるというやつだ。

たとえばGUIのボタンをカスタマイズしてマウスオーバーするとなにかちょっと特殊なことを行うボタンを作りたいとしたら、ボタンクラスを継承して、マウスオーバーのイベントハンドラをちょいちょいとカスタマイズしてやればよい。差分プログラミングは大変素直でよいプログラミング手法のような感じがする。

よいのはよいと思う。

しかしこういういい例だけをみてそれをどこでも真似しようと思ってしまうと、不必要な抽象化を積み重ねる困ったプログラマになってしまう(そういう人は結構たくさんいる)。自分でプログラムを書く場合には、よくできたクラスライブラリやフレームワークをお手本にして抽象化を行うのは、ほとんどの場合むしろよくないことだと思う。

なぜかというと、よくできたクラスライブラリは不特定多数のプログラマが使うことを前提に設計されているので、いろんなところでうまく継承関係などをつかって、必要十分な(ライブラリ作者には未知のニーズも満たせる)柔軟性を提供するようにできているからだ。

一方で、自分のすべてのコードが自分の管理下にあるのなら、そのような柔軟性は不要というか過剰だ。不要な抽象化を行って素晴らしいクラス構造を作ってもコードの量が増えてしまうだけだ。外部のクラスライブラリやフレームワークと違って自分のコードは自分で変更できるのだから、新規の変更でも必要に応じて既存のコードを変更すればよい。逆に言うと、あとで既存のコードはいくらでも変更できるのだから、最初から凝ったことはしないほうがよい。

新しい変更が一箇所にまとまる、というのは別にコードの良さという観点では大して重要ではない。全体として素直なコードになっているほうがずっと重要だ。(新しい機能が新しいクラスを定義するだけで実装できるというのは、特にソースコードのついてこないクラスライブラリだと必須条件だが、自分でコードを管理している場合は関係がない。)

「クラスライブラリを使うときにクラスライブラリのほうのコードを変更する機会はない。メジャーなクラスライブラリはよい設計だとされている。従って自分でクラス階層を作るときにもそれと同じようなものを作るべきだ」という考え方は誤謬だ。自分の具体的なプログラムはクラスライブラリやフレームワークのような抽象的なものとは違う種類のプログラムだ。考えすぎるのをやめて、必要なことを素直なやり方で直接実装するのがよいと思う。

Rui Ueyama @rui314


2014/12/28

正しくオブジェクト指向できているどうかという意味のない議論

僕はプログラマをしていて、数千万行以上の規模で10億ユーザ以上が使うようなプログラムの開発にもかかわっていたりしたけど、そういう仕事環境で「(ある何かが)オブジェクト指向かどうか」という議論をほとんどしたことがない。デザインのレビューでもAPIが十分シンプルかどうか議論にはなるけど、そのやり方がオブジェクト指向かどうかなどという観点でものを見る人はいなかった。日頃のコードレビューでも、やるべきことが普通にわかりやすく行われているかという観点でコードを見るのが普通で、オブジェクト指向ではどう、ということをいう人はいなかった。

一方でプログラミングの入門書などをみると「オブジェクト指向とはなにか」という説明に多くの分量が割かれていて、オブジェクト指向というものが、なにかまるである時点で悟りを開くように理解すべきものであるかのような解説がなされていることがよくある。しかもその解説が哺乳類と犬と猫みたいな、プログラミングですらないものだったりする。

一体このギャップはなんなんだろう? と思っていた。昔は、これだけまじめに議論しているひとたちがいるのだから、何らかの価値がそこにあるのだろうと思っていた。いまはあまりそうは思っていない。完全に否定しているのではないが、考えすぎても意味がないものだと思っている。

考えすぎることには害もある。いわゆるオブジェクト指向機能を濫用するとむしろコードは読みにくくなってしまう。まとめて一つの関数で一つの操作として書けばすむものを、無理に「多数のオブジェクトがメッセージを送り合って実行が進む」ように書いたら、単にスパゲティみたいなコードになるだけだ。たくさんあるプログラミングパターンも、プログラミングパターンを使うコードはよいコードという意識でいると、不要な抽象化を積み重ねてしまってコードが複雑になってしまうだけだ。

プログラムをリーズナブルな小さな単位に分解するというのはよい。そこで必要なのは「コードをなるべく単純で理解しやすくする」という意識だけだ。作るべき機能があるなら素直にそれを実装すればいい。「オブジェクト指向ができているか」みたいな考えは、本来の目的とは別の不要な評価軸を持ち込んでしまって、本来の目的の妨げにすらなってしまう。

「オブジェクト指向プログラミングとされているものはひととおりわかったが、いまいち本当のオブジェクト指向がなんなのかよくわからない」という状態になったら、別にそれ以上は悩む必要などないと思う。だいたいそれですべてで、僕の知る限りそれ以上の悟りのようなものはないのだから。

僕の書くコードも他人からみれば十分に「オブジェクト指向でき」ているんだろうとは思う。でも別にそれを目指しているわけでもないし、できていようができていなかろうが気にしていない。わかりやすくまとまっていて、不必要に複雑なことはしていなくて、十分にテストされているかどうかだけをだいたい気にしている。

Rui Ueyama @rui314