なぜソースコードは美しくあるべきなのか

Fujiwoさんが「美しいソースコードのための七箇条」というエントリを書かれた。グッジョブです、Fujiwoさん!

「美しいソースコードのための七箇条」というのを考えてみた。
# 取り敢えずの叩き台として。

「美しいソースコードのための七箇条」 (プログラミング C# - 翔ソフトウェア (Sho's))

ただ、ちょっと思ったのは、こういうエントリって既に美しいソースコードの書き方を(ある程度以上)知っている人にしかウケないんじゃないかしら、という心配。だから、「七箇条」を補完する意味で、「なぜソースコードは美しくあるべきなのか」を書いてみたい。うまく書けるかなぁ・・・。

美しい == 機能的

まずここで言う「美しい」ってのは、雄大な自然が美しいとか、美しい芸術とは意味が違う。「機能的なものは美しい」とかっていう言葉があるけど、そういう意味。雄大な自然は理屈ぬきで美しいけど、美しいソースコードは背景に理屈がある。理屈抜きには語れない美しさ。だから「美しいソースコードのための七箇条」は「機能的なソースコードのための七箇条」と言い換えてもいい、はず。なんだけど、やっぱり「美しい」と言いたくなっちゃうんだね。
なぜ美しいと言いたいかというと、ソースコードの美しさを体で理解した人にとっては、それはもはや左脳ではなくて右脳で感じる機能性になっているからだ。理屈抜きには語れない美しさのはずなのに、理屈ぬきに美しさを直観してしまう体になっている。でもそういう体になっていない人にとっては、「ソースコードが美しい」なんて気持ち悪く感じてしまうかも。だからそういう人は「美しい」を「機能的な」に置き換えて読んで構わないと思う。

守備としての美しさ

サッカーやバスケと同様に、ソフトウェア開発にも攻めと守りがある。攻めはゴールを決めること。つまり目的(ゴール!)となる機能を実現することであり、競合製品に対して優位に立つことであり、更に言えばその機能が売れてお金になることだ。対して守りとは、品質を落とさず長期にわたってメンテナンスや運用を継続していくこと。そして、ここでいう「美しいソースコード」というのは「守りのための機能性を備えたソースコード」と考えれば良い。
敢えて言い切ってしまうと、守りに理論はあるが、攻めに理論はない
スポーツでもそうじゃないかな。守りよりも攻めの方が才能が要求される。守りは地道な練習の成果だが、攻めは天才による芸術的なプレーに拠るところが大きい。「こういう練習をすれば誰でも芸術的なプレーが出来る」という理論はないんだ。
でも、守りには理論がある。そして守りが攻めのリズムを作る。リズムがないと芸術的な攻撃は出来ないのだ。だからスポーツでも守りの練習は重要だし、ソフトウェア開発でもそうだ。だから「美しいソースコード」といえばそれは守備の美しさなのだ。

美しい守備がもたらすもの

守備なんてつまらないという人もいるだろう。芸術的な攻撃的プレーに憧れているんだ、という人もいるだろう。でもね、そういう人にこそ美しい守備が必要なのだ。まず次の事実も知って欲しい。
プログラミングの時間の大半は、実は既存のコードを読んだり理解したり、あるいは修正したりといった作業に費やされている。純粋に新規開発に当てている時間は直観的に思うよりもずっと短い。例えばある機能の開発に取り掛かったとする。その機能にはAモジュールに定義されたBクラスを使う必要がありそうだ、じゃあBクラスの使い方は・・・、といった感じで、既存モジュールの既存クラスを調査することになる。純粋な新規開発はわずかだ。
既存のコードを理解したり修正したりする作業を「守備」、純粋な新規開発を「攻撃」と捉えてみよう。すると上記の事実は次のように言い換えられる。「ソフトウェア開発の大半は攻撃よりも守備に追われている」。
さあ、ここで本題だ。なぜソースコードは美しくあるべきなのか。それは守備に費やす時間を出来るだけ減らして、多くの時間を攻撃に回すためだ。ゴールを決めなきゃゲームには勝てない。でも時間は限られている。だから出来るだけ攻撃に時間をまわさないといけない。だからこそ、美しい守備を心がけるべきなのだ。

美しいコードはコントロールしやすい

さっき守備を「既存のコードを理解したり修正したりする作業」と書いたけど、要するに守備とはコードをコントロールすることだ。支配することだ。守備がゲームを支配するんだ。
美しいコードはコントロールしやすい。というか、正確には、熟練したプログラマはコントロールしやすいコードを美しいと感じる。コントロールしやすいコードが機能的なコードであり、美しいコードなのだ。
美しいコードとは読みやすいコードのことではない。熟練プログラマが書く美しいコードは、大抵の場合初心者には読みにくい。読みやすいとか読みにくいという指標は、読む人の技量や好みに左右されてしまうので、僕はあまり「読みやすい」という表現は使うべきではないと思っている。それよりもコントロールしやすいという方が誤解が少ない。
ではコードをコントロールするってどういうことか。僕はコントロールには大きく分けて次の2種類があると思っている。

帰納的なコントロール

あなたにある関数が渡されたとしよう。その関数の仕様について何の説明も受けていないし、ソースコードにコメントもない。その関数を、中身のソースを一切読まないで動作を理解し自分の支配下に置くことが帰納的なコントロールだ。
帰納的なコントロールにおいて、その関数は完全にブラックボックスだ。中身の見えない箱を眺めたり叩いたりして、その関数の動作を検証しなくてはならない。その関数がどんな性質を持っていれば帰納的にコントロールし易いだろうか。幾つか思いつくものを並べてみよう。

  • 名前。関数の名前は機能の重要なヒントだ。
  • 引数は少ない方が調べやすそうだ。
  • 引数を2〜3パターン試す程度で予測可能な動作なら分かりやすい。逆に色んな引数を入れて試してみてもなかなか傾向がつかめない関数はコントロールしにくい。

などなど。

演繹的なコントロール

演繹は帰納の対義語だ。だから逆に考えればいい。
あなたにある関数が渡されたとしよう。その関数の仕様について何の説明も受けていないし、ソースコードにコメントもない。その関数を、外側からのテストを一切行わず中身のソースを読むだけで動作を理解し自分の支配下に置くことが演繹的なコントロールだ。

コントロールを失う過程

放って置くと、いつの間にかソフトウェアのエントロピーは増大し、プロジェクトはアンコントローラブル(制御不能)に陥ってしまう。僕の経験では、帰納的なコントロールよりも演繹的なコントロールの方が壊れやすいし、帰納的なコントロールを保つことはとても重要だ。演繹的なコントロールを失うと、次のようなことが起こる。
あるフラグの意味を知りたいとしよう。最初はそのフラグの周辺のコードを読み始める。つまり演繹的なコントロールを試みる。ところがスパゲッティコードでさっぱり意味が分からない。そこであなたはこう思いつく。そうだ、試しにこのフラグを1から2に変えてみよう。その状態でソフトウェアを起動してみて、どんな動作になったかを確認すればいいじゃないか。
これはまさに帰納的なコントロールだ。確かにフラグの意味は把握できたかもしれないが、ソフトウェアがあなたのコントロールから静かに零れ落ちようとする気配を感じないだろうか。これが不吉なにおいだ。ここでリファクタリングして演繹的なコントロールを取り戻さなくてはならない。そうしなければ、いずれ帰納的なコントロールも失い、制御不能に陥るだろう。

まとめ

なんか色々と漏れがありそうだし、議論も荒っぽいかな。まあいいや、叩き台として。