2006-07-28

近況

先週から体調が悪い.

体調が悪いときはまず本を読む. あまり疲れないもの, 新書や軽めのノンフィクション. 技術書には手が伸びない. 手持ちの本が尽きると web を見てしまう. しかも "あとで読む" の消化などはしない. b.hatena.ne.jp などを上から眺めたり, 他人のアンテナなどを頼りに人気のあるページを見たり. ああ, 不毛だなあ...

そんなかんじでうろうろしていたら, failmalloc というのを見けた. (作ったひとの日記.) 前の会社でも似たようなものを使っていたと思いだす. このごろ前職の話が多い気もするけれどまあいいや...

failmalloc (相当) のおもいで

failmalloc は malloc() を override して意図的にメモリ確保を失敗させるツールだ. メモリ確保エラーのチェック漏れをさがすのが狙い. 私の使っていたフレームワークも failmalloc とよく似た仕組みを持っていた. 意図も同じ. その仕組みは昔からあったらしいけれど, あまり広くは使われていなかった. 使ってみるとその理由がわかる. ポロポロとバグが見つかるのは最初のうちだけで, そのうちバグを見つける前にプログラムが終了するようになる. バグが出ないとつまらない. だからやがて使わなくなる.

バグがみつからないのはバグがなくなったからではない. バグが出る前に, メモリ不足のエラー処理としてプログラムが終了してしまう. たとえば

int main() {
  int e;

  e = setup()
  if (E_OK != e) {
     printf("error!");
     return e;
  }

  e = body();
  if (E_OK != e) {
     printf("error!");
     teardown();
     return e;
  }

  teardown();
  return 0;
}

みたいなコードがあるとして, setup() の中でエラーになると body() が実行される前にコードから抜けてしまう. その結果 body() の中のエラーはチェックできない.

実行を body() まで続けさせるには色々パラメタを調整する必要がある. failmalloc には以下のようなパラメタがある.

INTERVAL や SPACE をうまく調整すれば body() までエラーを起こさず粘ることはできるだろう. でもその調整はけっこう面倒. 社内の類似ツールがいまいち流行らなかったのも, その手間が理由だとおもう.

さて, あるとき私はテスト環境の強化に情熱を燃やしていた. (そんな年頃だったのです.) その一環として failmalloc(相当) も試したのだが, 上のようなかんじでぱっとしない. 色々議論した末, その failmalloc(相当) をちょっと改造し, "特定の関数の中でだけ" エラーを起こせるようにした.

社内フレームワークの別の機能として, mpatrol のような メモリーリーク検出機構があった. mpatrol はメモリ確保時のコールスタックを記録し, リークがあった場合はログからその箇所を特定する. (似たような仕組みは Google perftools のようなヒープ・プロファイラにも入っている.) 私はそのコードを適当に切り貼りして件の改造を行った. まず, failmalloc の中でコールスタックを覗く. で, スタックの中に指定された関数があったら(確率的に)失敗させる.

この機能を試すべくスモークテストをしたら, 私のコードからは案の定バグがどさどさみつかった. やっほーごめんなさいという気分. フレームワーク自身には思ったほどバグはなかった. 叩かれて枯れていたのだろう. 失敗する関数名は API で指定することができた. だからみつかったエラーはまずその失敗 API を使った単体テストで再現し, それから直す. これが典型的な クラッシュ・グリーン・リファクター となった.

エラーチェックの文化

前職の組込み開発ではバグの大半がエラーチェック漏れだった. 今の職場は(私は違うことをしているけれど) 受注のデスクトップアプリケーション開発が主な仕事. で, どうかというと, 結局ここでもバグはエラーチェック漏れが大半に見える. PC のアプリケーションらしく, 環境によって動かないパターンが多いようだ. 仮想記憶があるから malloc() は失敗しないけれど, その他の様々な API がたびたび失敗する. だから組込みだろうが PC だろうがエラーチェックは必要だと私は思う.

ただ今の職場コードはエラーチェックが甘い. アプリケーションもよく落ちる. (個体差はある.) ミッションクリティカルでない受注アプリという性格を考えるとこういうものなのかもしれない. アプリケーションは落ちるもの. デスクトップの世界ならそれもアリなのはわかる. 扱うデータも厳密に保護する必要のないものだ.

一方でバグが報告されたら直す必要がある. これはデスクトップも組込みも同じだ. 違うのは再配布のコスト. 再配布でダメージを受けるのは企業そのもので, プログラマ自身ではない. (自身もそれなりに困るけれど, その痛みは被害総額の比でない.) 要するにプログラマにとってだけのバグのコストは, デスクトップと組込みで大差はない. 私はバグ追跡が大嫌いだから, 環境が何だろうと VERIFY() マクロなどで固めてエラーを早めに見つけるよう コードを書く傾向がある. 私はこれを自分のためだと思ってやっていたけれど, 実はバグ嫌いの性向自体が業務によって埋めこまれたものなのかもしれない.

まあ, assert() や verify() くらいみんなやればいいのにとは思うけどね...

バグのコストの内と外

私はデバグが嫌いだからそれを予防するコードを書く. より一般的には, デバグ=バグ修正のコストが高いから バグ予防にコストをかけられるとも言える. 割に合うというわけ.

このトレードオフを正しく働かせるのはけっこう難しい. バグを作る側がバグ修正のコストを本来の比率で負担する必要がある. 先の組込みの例でいうと, バグ修正のコスト(正確にはバグが発覚した場合のコスト)は プログラマ以外の関係者が広く分散して負担する. 再テストで徹夜するテスタ, 取引先で土下座する上司, ROM を再配布する代理店, など.

そのほかには, ライブラリアンとアプリケーション開発者の間にもアンフェアがありうる. ユーザからのバグ報告は大抵アプリケーション開発者が受けとる. アプリケーション開発者は原因を特定し責任範囲を切り分ける必要がある. そしてライブラリのバグだけがライブラリアンに報告が伝えられる. バグは直す以前に原因を調べるのが大変なのに, ライブラリアンはその苦労を負担していない.

逆に組込みプログラマやライブラリアンが不当に苦労することもある. たとえば...例をあげるまでもないか...

このようにコストが 外部化 されリターンとの関係がいびつになると, 市場はうまくまわらない. バグの場合も同じ. 逆にこの外部性をうまく内部化することができたら ソフトウェア開発の効率があがるかもしれない.

たとえば, ライブラリアンはライブラリだけでなくアプリケーションもつくれとか, エースはドキュメント書けとかロードマップを明らかにしろとか. 色々考えることはある. ただバグをつくった組込みプログラマは工場に行って ROM を焼けというのは やや不憫すぎるし現実的でない. だから保険が必要になる. テストやレビューなどのいわゆる "開発プロセス" がその保険だと思えばいい.

一方でソフトウェア開発の現場はそこらの市場以上に効率的市場仮説とかけ離れている. 情報は限りなく不透明で, デスマーチの現状に何かが折り込まれているとはとても思えない. いっそ市場原理は諦めて社会主義でいこう. 重量級の開発プロセスはそんな意思の表れと見ることもできる. 保険としての開発プロセスと矛盾するようにも見えるけれど, 社会主義をモノシリックな共同体に対する掛金一律の保険だと思えばそう遠くもない. (言葉の綾なので関係者は怒らずスルーしてください.) またプログラマに視線を移すと, そこに市場主義的な意味での優れた主体を見るのは難しい.

アジャイルと市場主義

重厚な開発プロセスを社会主義的だとすると, アジャイルな開発スタイルは新古典経済学スタイルだと言っていいかもしれない. このところ, 私はそんな気がしてきている. YAGNI なんて典型的. ステークホルダー(顧客)重視もそう. ほかには, スタンダップミーティング, 頻繁なリリースで情報をオープンに. コードの共同所有で市場を流動的に. など.

無駄の多いソフトウェア開発の現状を考えると しばらくアジャイルの勢いは続くだろう. ただある程度合理化が進んだ時には 極端な市場主義の生むきしみが問題となる日が来るかもしれない. セレブアーキテクトとコーディング・プアの賃金格差, スーパープログラマの粉飾, テスト・エンジニアのモラルハザード... なんてのはまあ半分言葉遊びで, あまり心配はしていない. ベスト・プラクティスだけを眺めると合理主義が目立つけれど, アジャイルそのものにはある種の人間性が通底しているから.

とはいえアジャイルがもつこの二つの特性: 人間性と合理主義の間には, 本質的な緊張関係がある. その均衡が崩れる危険は常にあると思う. それを乗りこなすバランス感覚を, ひとは pragmatism と呼ぶのかもしれない.

まとめ: