読者です 読者をやめる 読者になる 読者になる

Web プログラミングってこんな感じじゃなかろうか

設計 テスト

 この歳になって初めて Web プログラミングの現場を見て、刺激的な毎日を送らせていただいています。さて、仕事をしていて一番強く感じるのは前職での開発(スタンドアロンパッケージソフト開発をしていた会社の文化)との違いです。で、Web 開発とは何たるかを表現したくて悶々としていたのですが、ある程度整理ができてきたので文章にしてみようと思います。僕はアカデミックな話よりも現場の話をしたいので、いくつもレイヤをまたいだ文章になります。そのため本稿では具体的な技術の詳説とかアジャイル的な用語が飛び交う説明とかはありません。そういうのを期待している人は読まないでください。

・Web に限らず、お仕事プログラミング全般で共通のこと
 プログラムを知らない人がイメージする開発というものは、粘度をこねたりくっつけたりするような作業じゃないでしょうか。つまりそれは、知識さえあれば難しいものではなく、モチベーションさえあればいくらでも素晴らしい製品が作れる、という性質のもの。しかし実際は「ここに飾りをつけて見栄えを良くする」「アレとコレを混ぜて化学反応を起こす」みたいな作業は総じてバグの元になりやすいです。どちらかというとパズルのピースをはめ続ける作業の方が間違いが発生しにくく、生産性は高いです。例えば、A か B のどちらかの値を埋めてね、という場面で A や B を埋めるコードを書くことは簡単ですが、A か B か C のいずれかの値を埋められるように枠組みを変化させるためのコードを書くのは少し難しくなります。

・ピースをはめる作業の割合が大きければ大きいほど安全
 もし仮に 2 のソフト生産ラインがあったとして、全く同じ製品を同じ人が同じ品質になるまで作るとします。違うのはパズルのピースをはめる作業の割合だけ。そうすると、ピースをはめる割合が高いほど短い期間でできるはずです。だから、そういう理由で Web フレームワークの選択がプログラミング言語の選択よりも重要になるケースも多いと思います。例えば Rails のようなものが選ばれやすい。で、こういう話の流れで Rails を例に挙げると、現場のエースの方々から「レールに乗っている間は楽なんだけどレールから外れるといろいろ面倒だから話にならん」という脊髄反射が飛んでくるわけですが、それについては次節で続けるとして、とにかく開発のとっかかりレベルでは Web フレームワークの選択は超重要。

・そもそも言語がだめだとフレームワークは生まれない
 コア言語に乗っかって開発される Web フレームワークは、言語の特色が色濃く反映されます。Python が無かったら Zope も Django も生まれていないし、Ruby でないと RailsSinatra も存在しえない。PHP だと Cake とか Zend とかかな。良いフレームワークを産出した言語というのは良い言語であると言えると思います。そして前節でも触れたように、フレームワークを使うと、敷かれたレールを外れたところで生産性がガタッと落ちます。そのときプログラマパズルのピースをはめるだけで開発が進められるようにレールを敷く作業をします。ここでプログラミング言語の習熟度が現場の開発に影響してきます。つまりフレームワークを使いこなすレベルではプログラミング言語の習得は超重要。

・「昨日の品質 > 今日の品質」にしないためのテスト
 昨日のアプリの品質が、今日の作業で悪化する、ということはままあります。「昨日の品質 > 今日の品質」ということです。で、それが実はレアケースのバグだったりして、しかも運悪くバグに気づかずにがんがん開発進めちゃって、後から痛いしっぺ返しを喰らっちゃう...っていうのはダメダメですね〜、ってことで CI やらテストやらの話が出てくると思うのですが、僕が気にしているのはテストの存在意義。テスト超重要ですね。

・テストの意義と静的型付け言語
 Twitter とかでたまに巻き起こる「テストはなぜ必要か」的な大づかみで的外れな議題で盛り上がるときにしょっちゅう出てくるのが静的型付け言語との比較です。もちろん「言語とテストは別だろ JK」的な指摘は踏まえた上で話を進めたいのですが、「テスト」と一口に言うと語弊があるのであえて面倒くさい表現をしますと、ここで言うテストとはプログラマの些細なミスで「昨日の品質 > 今日の品質」になってしまわぬように準備されるテストコードのことです。それを ホワイトボックステストブラックボックステスト の 2 つにざっくり分けて考えると、前者が静的型付け言語によってある程度フォローアップできる*1一方、後者はできません。この区分けをとりあえず飲み込んで次節に飛びます。

・Web は文字列の世界であり、Web 開発は速い
 なんだかんだ言っても出力は HTML と CSSJavaScript です。いずれも文字列をベースとした技術です。また Web アプリはレイアウト処理やイベントハンドリングなどをブラウザから強力にフォローしてもらえるので、ガンガンページを飛べたりガンガン DOM 構造を変更できたりします。小さいものを作り込んで部分的な質を上げるよりも、手広く量をこなした方がアプリケーション全体の価値を上げやすい性質があり、開発は比較的速いような気がします。で、そうなると Array や Hash をこねくり回したり文字列を切り貼りするような処理が多くなります。細かいデータにいちいち〇〇型とか名前をつける事が bad と判断される場面は多いはずです。つまり静的型付け言語でフォローアップできない領域がやたらと広い*2

・テストのあり方
 問題を分割するのがエンジニアリングの基本とはいえ、こうしたあらゆる背景を環境変数とみなしてプロジェクト全体を俯瞰すると、必然的に有効なソリューションとしてテストというキーワードが浮上してきます。繰り返しになりますがテスト超重要。で、テスト*3について入門的に説明している Web サイトを見ると、V字モデルを理解することが大切だとかテストの種類を把握することが大切だとか、そういう事が書かれています。テストの用語についての厳密な議論は @t_wada さんとか @kyon_mm さんとか @bleis さんとか @irof さんとかに直撃インタビューしてお聞きするとして、ともかく「昨日の品質 > 今日の品質」にならないためのテストコードをどう書くか?という疑問に対しては、何らかのコンセプト的回答を用意しないと始まらない気がするのです。それは「RSpec でこういうマッチャーを使いましょう」とか「Eclipse のこういうプラグインを使いましょう」とかいう特化した話ではなく、例えば「グローバル変数はできるだけ使わないようにしましょう」とか「クラス設計ではできるだけカプセル化しましょう」とかいうような、もっとずっと汎用的なコンセプトです。そういうものが、耳にタコができるくらい謳われてしかるべきです。

・テストは失敗する事に最大の価値がある
 恐らく、ですが、テストの価値は失敗にこそあるような気がします。成功を 100 回繰り返すためではなく失敗を 1 回検出するためにテストを書くわけです。であるなら、テストは正しく失敗するべきでしょう。ここでいう正しい失敗とは、正しいテストコードが検出した失敗です。例えば RSpec で should 指定した部分で検出した失敗や JUnit で assert 指定した部分で検出した失敗は正しい失敗です。一方、正しく無い失敗とは、正しく無いテストコードが吐いたエラーです。例えば RSpec で存在しないクラスの new を呼んで落ちたとか、JUnit で assert の手前のプログラムで予期せぬ NullPointerException が発生したとかいうのは、テストが狙った機能を果していないわけで、それは正しく無い失敗でしょう。正しく無い失敗でコケたテストコードはそもそも間違っているので、たまたまコケただけに過ぎず、たまたま通ることだってあるだろうからです。まぁつまり前節で僕が欲しいと言ったのは、例えば「Fail はどんどん出しましょう。しかし Error は断固潰しましょう。」というコンセプトです。*4

・まとめ
 ここまでが、最近ようやく僕が考えるようになった、Web 開発で生産性を確保するために最も重要な 3 要素です。

  1. フレームワーク   ⇒ できるだけレールの上でパズルのピースをはめ続けるようにするべし
  2. プログラミング言語 ⇒ 1. を促進するために、レールから外れたときにできるだけレールを敷くべし
  3. テスト       ⇒ 1. 2. を促進するために、失敗を狙い撃ちするテストコードを書くべし

・とまぁ今のところこんな感じなのですが
 僕はずいぶんと飲み込みが遅いので、数ヶ月後にもっと大切な気付きを得るのかも知れません。開発環境とか、インフラとか、Twitter で皆さんよく盛り上がってますし。世間の会社さんはどういう結論を出してるんでしょか? Twitter を見ている感じだと、こういう話は一部の会社だけが「ウチはこうだ」と決めているような気がします。*5

*1:「何言ってるんだ!?例えばメソッドの引数の境界値をJava のソースからコンパイラが自動判別してテストしてはくれないだろう?」というツッコミはスルーしますね

*2:yesed?そんなものは知らぬ!

*3:アプリの実装を書く前に書く Developers Test じゃなくて品質を不用意に落とさないようにするためのテスト

*4:別にこれじゃなくて良いんですけど、何か欲しいのです。

*5:あぁそれから、給与とか残業とかマネジメントとか顧客との打ち合わせとか会議の進め方とか朝会を何分やるかとかそういう話は別の領域の話だと思うので意図的に触れていません。あしからず。