バッドシグナルとは?
今号から「バッドシグナル通信」と題して、連載することになった高林と申します。今回からは、ソフトウェア開発の中で見られる「バッドシグナル」について書いていきたいと思います。
ソフトウェア開発を行っていると、「何かいやな予感がするなー」と直感がはたらいて、それが的中することがままあります。いやな予感には何かしらの原因があり、そこから発せられる危険信号を人は読み取っているのではないかと思います。筆者の周りではこの危険信号を、バッドであることが予想されるシグナルという意味で、「バッドシグナル」と呼んでいます。筆者はこの言葉を使い始めてからは、いやな予感がするきは「このバッドシグナルは何だ?」と考えるようになり、いやな予感の原因を特定する習慣がつきました。
本連載ではありがちなバッドシグナルを取り上げて、その対処法などを検討していきたいと思います。
第一弾のバッドシグナルは「歴史的理由」です。
For historical reasons
ソフトウェアのソースコードや文書を読んでいると、「For historical reasons」(歴史的理由により)という言葉によく出くわします。試しにGoogleソースコード検索で「For historical reasons」を検索してみると95,100件もヒットしました。最初のほうの何件かを読んでみると、こんなものがありました。
"For historical reasons, these macros are grosslymisnamed." -Linus
「歴史的理由により、これらのマクロはおぞましく名前が付け間違えられている。」- Linus
※ linux-2.4.34/include/asm-sparc/uaccess.hより引用。日本語訳は筆者。
これは歴史的理由の典型的な例です。パブリックなAPIに変な名前を付けてしまうと、後から変えようと思っても変えられません。すでにそれらのAPIを使っているクライアントコードが無数にあるためです。
もし下手に変えてしまうと、クライアントコードのコンパイルが通らなくなって「勝手に変えるんじゃねー」と怒った開発者が大挙して押し寄せてくる恐れがあります。他人の変更のせいでコンパイルが通らなくなるのは腹立たしいものです。
名前が変わってコンパイルが通らないだけなら対処は比較的簡単ですが、Pythonなどの動的言語では、実行するまでエラーがわからない場合があるので、問題はより深刻です。
こういったAPIの問題に対しては、新しいクリーンなAPIを追加して、古いAPIは「deprecated」(非推奨)という印を付けて、一定期間後に削除するという方法があります。完全な解決策ではありませんが、いきなり変更するよりはダメージを防げます。
安易な仕様
歴史的理由がはびこるのは、APIだけではありません。安易に設計した設定ファイルやコマンドラインオプションは歴史的理由が好んで生息する危険ゾーンです。
ありがちなのはこんなケースです。
「よし、--inputというコマンドラインオプションで入力のファイル名を指定できるようにしよう」
(数日後)
「複数の入力ファイルを読み込めると、もっと便利だよなー。しかし、どうやって複数のファイル名を渡そう。とりあえずカンマで区切って渡すことにするか」
(数か月後)
「うわ、なんかユーザから苦情が来た。カンマを含むファイル名を読み込めないって? そんなファイル名付けるなよ。んー、どうしてもその名前を変えられないの?あー、歴史的理由かー。とりあえず、カンマを\でエスケープできるようにしとくよ」
(数か月後)
「うわ、またファイル読めないって怒ってるユーザ来たー。これ、ファイル名に\を含んでいるじゃん。これは\\ にしないと。というか、なんで\ なんか使ってるの?あー、Windows なのか。\\ がキモイといわれても困るなあ。いまさら、挙動変えられないすよ」
このケースの場合、「とりあえずカンマ区切り」と「とりあえず\でエスケープ」という2つの歴史的理由が重なってWindowsユーザがえらい目にあっています。今となっては--inputの挙動を変えるわけにもいかず、厄介です。
敗因は、--inputというオプションで複数のファイルを指定できるようにしたことでしょうか。grepコマンドの構文grep [options] PATTERN FILE...
と同様に、コマンドライン引数の後ろの部分(FILE...
)を使って任意の数の入力ファイル名を指定できるようにすれば、このような問題は起こらなかったはずです。
手抜き実装
APIや設定ファイルは外から見える仕様ですが、実装の内部にも歴史的理由はよく潜んでいます。手抜きの実装が、将来にわたって禍根を残し続ける場合が多々あります。
先ほどのプログラマ氏なら、こんなことになりがちです。
「メールのアドレス帳を作るぜー。表示用の名前TaroYamadaとメールアドレスtaro@example.comがあれば十分かな。こういうのはクラスにしてdisplay_nameとaddressと2 つのフィールドを持たせればいいんだよな。でも、クラス書くの面倒くせー。待てよ、これ、"taro@example.com Taro Yamada"みたいに、1つの文字列に空白で区切って入れとけばいいのでは? どうせアドレスは空白を含まないんだし。急いでるからこれでいいやろ。メモリも節約できてクールだぜ」
(数か月後)
「なんか苦情きたー。えええー、メールアドレスって"taro.(空白)yamada"@example.com みたいなやつありなの? これって空白含んでいるじゃん。RFC[1]読むかなー。うわ、やっぱこれありなのかよ。しかし、いまさらどうしろっていうの。データベースにこの形式でもう保存しちゃってるし、いまさら、変えられないぜ。仕方ないから、"taro.(空白)yamada"@example.com みたいなメールアドレスをちゃんとパーズ(parse)するコードを書くか。面倒くさー」
このケースでは、またもや変な区切り文字のルールの導入という歴史的理由で悲惨な目にあっています。さすがに二度も痛い目にあうと、「区切り文字」と聞くだけでバッドシグナルを嗅ぎ取ることができるようになります。
地雷ハック
ある特殊な条件で起きるバグを直したい場合など、その場しのぎのハックで対処したくなるときがあります。このようにして入れたハックが地雷となり、後々になって災いを及ぼすことがあります。
また、例のプログラマ氏に登場してもらいましょう。
「うげ、なんかうちのサイトのレイアウトがIEのときだけ崩れるなー。いったい俺の時間はIEのためにどれだけ浪費しているんだ。さっさと片付ける方法ないかなー。んー、なんかCSS のアンダースコアハックなる技を発見! これを使えば、IE だけに適用できるスタイルを書けるのかー[2]。これはバッドだけど使える!」
(数年後)
「うわ、なんか苦情がきたー。IE でレイアウトが崩れるって? おかしいなあ。たしか数年前に直したような記憶が。あれ、このアンダースコアハックってIE7 だと無効なの? 聞いてないよそんなの。かといって、これいまさら削るとIE6でのレイアウトが崩れるからなあ。これ入れたまま、IE7のレイアウト直すのはどうすればいいいんだろう。おや、IE はエレメントにhasLayoutという隠しプロパティがあるの?[3] もしかして、この情報を使えばなんとかなるっぽい? あ、なんかよくわからんけど、できたよ。やれやれだぜ。俺はもう燃え尽きたよ」
(数年後、後任者登場)
「最初のプロジェクトは、IE8 でのテストですか? いいですよ。ちょっと、試してみますね。あ、レイアウトが崩れますね。ちょっとコードを見てみます。うわ、なんなんですかこれ。このアンダースコアは何? このhasLayoutという謎のプロパティは? これもハックなんですか? しかも、いまどき何でEUC-JP[4]なんか使っているんですか。誰ですかこんなの書いたの。あ、辞めちゃったTさんですか。しかし、これ、ハックだらけでコードぐちゃぐちゃですよ。これ、どうやってIE8対応したらいいんですかね。え、私がやるんですか?」
これはまさに泥沼の状況です。前任者はすでにいないのでハックの真相はわかりませんし、後任者にとっては悪夢です。特定のブラウザだけで使えるハックは、問題を抱えているときには非常に魅力的な解決策ですが、このように将来に禍根を残す危険性があります。こうしたハックにできるだけ頼らずに問題を解決したいところです。
まとめ
今回は、歴史的理由というバッドシグナルを取り上げました。一度、判断を誤って変なコードを入れてしまうとそれを取り除くのが非常に困難になることが多々あります。このような歴史的理由を作らないよう、常日頃から気をつけたいものです。