ソフトウェア・エンジニアの語る、虚々実々の物語

ドキュメントの錬金術師

»

 錬金術…。錬金術とは、人間の肉体や魂さえも対象にして、対象をより完全な存在に錬成する試みだそうです。普通ならば、そんな胡散臭いものを簡単に信じることはないのでしょうが、極限状態に置かれるとこういう話を信じてしまう人もいます。

 今回は、ソフトウェア開発現場では、こんな形で錬金術を信じる人間が現れることがある、という私の回想にお付き合いください。

■プロローグ

 あるところで、まだしっかり動作している巨大システムに、大きな改造を加える工事が入りました。対象となったシステムは、太古の化石のようなシステムです。そのシステムの開発当時のドキュメントなど、風化してしまっていて残骸しかありません。ただただ膨大なソースコードの原野が目の前に広がっているのみです。

 そのソースコードさえも、どれも死に絶えて久しい恐竜の化石のようなものです。ただし、実行モジュールだけは…ちゃんと動いていたのです。まさに生ける屍と言いますか、死んでもらっては困るので、無理やり生かしていると言いますか。

 私は当時、改造工事を担当することになった部署に所属しており、改造プロジェクトのメンバーに加えられ、見積もりも担当することになりました。しかし、当時の開発担当者はすでにいなくなっており、機能仕様を知るための手掛かりのほとんどはソースコードの中にしかないという始末。とてもまともな見積もりができるとは言えない状態でした。まさに「新規に作った方が早いんじゃない?」という状況だったのです。

 営業は、お客様に対して「新規に作り直します」とは言えず、金額面の交渉は難航していました。

 開発現場は開発現場で、埋もれた機能仕様をなんとか掘り起こせないかどうか苦慮していました。現存する機能仕様が分からなければ、どこから改造していいのか見当もつきません。当時は、派生開発などの手法は知られていませんでしたから、それこそ手探りです。

 このように、我々のようなぺーぺーが右往左往している中、誰かが言ったのです。「ソースコードから、ドキュメントを作り出せばいいじゃん♪」って・・・。

 ソースコードしか残っていないプログラムの山から、ドキュメントを作り出す。この場合、”創る”と言った方が正しいでしょうか。これぞ錬金術!どんな腐った(ふざけた?)ソースコードからでも、ちゃんとしたドキュメントを発掘できるはずだ!そんなことを本気で考えたプロジェクトが過去にあったのです。

 さっそく「ドキュメント再生プロジェクト」がスタートしました。コードを書くことには長けていても、ソースコードからドキュメントをリバース・エンジニアリングするのは、多くのメンバーにとって初めての経験でした。今回は、メモ程度では許してもらえません。れっきとした機能仕様書を創り上げなければなりません。

 プログラムの大半はC言語で書かれていました。Visual Basicで書かれたコードも多少ありましたが、SQLが大量に埋め込まれていて、とても手を付けられません。取りあえず、Cのソースコードから作業に取り掛かりました。

 しかし…連れ合い(ドキュメント)に先立たれ、そのコードは「すでに死んで」いたのです。腐っていたと言ってもよいでしょう。

 部長の肝いりでスタートしたプロジェクトですが、次々と問題が噴出します。果たして、噴き出す屍臭はどんなものだったのでしょうか? 以下をご覧ください。

●コメント臭

 まず、ソースコードにコメントがない。あったとしても、役に立つものなんて一切ない。以下のようなコメントしか付いていなかったのです。

 i = 0 ; // 0で初期化

 おいおい、コメントの左のコードを見れば一目瞭然なことを、文章にする必要があるのか? きっと当時のプログラム設計書に、「0で初期化」と書いてあったのでしょう。それを見ながらプログラマがコードを書いたのだと推測できます。

 初期化くらいなら、どうにか意図を読み取れないことはありません。しかし、

 i++; // 自動変数の増加

 とあっては、何を意味しているのかさっぱり分かりません。何のためにインクリメントが必要なのか、細い細い蜘蛛の糸をたどっていかなくてはなりません。そして、蜘蛛の糸は、途中で”必ず”切れてしまい、エンジニアは、血の池地獄に落ちてしまうのです。

 ああ、こんなコメントなら、ないほうがましです。ディスクのゴミが増えるだけですから。

●ネスト臭

 C言語の中括弧({、})のネストが異常なまでに深くなっているのです。括弧の中にまた括弧。その中にまた括弧…(以降、無限ループ)。そんな具合に、あっという間にネストが深くなっていきます。

 if文などを見ると、よくその現象が発生していました。

  if() {
    if() {
      if() {
        if() {
           (以降、無限if攻撃)
        }
      }
    }
  }

 深すぎるネストのおかげで、横スクロールが延々と続毛なければならないものもありました。もちろん、縦スクロールの量は10ページを楽に超えます。プログラムを紙に打ち出して、ネストを一つ一つ調べて、その意味を解読しようとしましたが、大体の場合、途中で力尽きてしまいました。

●スレッド臭

 Cのプログラムで、複数の処理を並列(プロセッサが1つなので、真の並列ではありませんが)に実行させるには、スレッドという技術を使います。まあ、プロセスでも良かったのですが、スレッドの方がプロセッサやメモリーにかかるコストが安いと考えて、スレッドを多用していました(と、思われた)。

 スレッドは便利ですが、この動作の意味するところを探るには、状態遷移図は欠かせません。プログラムの中には、無作為と思えるほど、スレッドを濫造するモジュールもありました。

 どういう意図でスレッド化したのか? その意味を延々と追い続けていたエンジニアも居ましたっけ。スレッド間のデータ受け渡しに、グローバル変数が使われていたときには、さすがのエンジニアも萎えていましたね。

●グローバル臭

 データはプログラムの動きを調べるのに非常に有効です。データテーブルがちゃんと定義されているだけで、かなりの情報が得られます。プログラムがデータ駆動型ならば、これでほぼ全容が分かると言っても差し支えないでしょう。

 しかし、テーブルなど存在せず、散乱しているグローバル変数だけでプログラムが動いていた場合には、あなたは何を思うでしょうか?
うう~ん。芳しい屍臭が漂ってきますね。

 そんな時、Grepを使って、グローバル変数を参照しているプログラムを総当りしますか? それとも、作成者を呪い殺すための藁人形を用意しますか?当時のエンジニアたちは、そのどちらもせずに、只々途方に暮れていたように思います。

●直値臭

 グローバル臭に近いものですが、この屍臭は、Defineなどを使わずに、プログラムのあちこちに直接数値を配置している「腐り方」です。中には、

 if ( i == 5+ 10+ 10+ 10+ x ) { … }

のように面妖な計算式が書かれたものまでありました。きっと、何かのインデックスを足しあわせたものであると思われますが、その数値の意味するところは、もう誰にも分かりません。こんな時は、どんな腐ったコメントでも有難いと思うかもしれません。

●Break臭

 While文を無条件に使用しまくると、発生する腐臭です。

 While(1) { … }

みたいな処理があちこちにありました。無限に処理を繰り返したい場合など、書きたくなるコードではありますね。

 でも、無限ってなんでしょうね? どこまで行ったら終りになるのか? その終わりっていうのが実は重要なのではと思ってしまいます。

 で、実際、無限に処理をするわけではないので、どこかで処理を終わらせる必要があります。その時に使うコマンドが

 break;

です。

 この一行で、処理中にループの外に飛び出すことが出来ます。まさに「ブレーク」してしまうわけです。さて、While文が1重だったら、読み解くのは楽です。

 しかし、これが

 While(1) {
  While(1) {
   While(1) {
    While(1) {
     … }
    … }
   … }
  … }

って感じだったら(笑)。そして、あちこちに、Breakコマンドが散乱していたら…。もう、そこら辺のものを破壊したくなりますよね。実際に、頭にきてロッカーを壊した奴もいました。(笑)

 そして、for文でも、同じような無限ループが作れます。こっちの場合は、もっと意味不明ですね。なぜfor文で無限ループなのでしょうか?人智を超えた産物なのでしょうか。深く突っ込むことが出来ません。

●ジャコウネズミ臭

 これは、私が勝手に命名したものです。一般的な呼び方ではありません。

 で…ジャコウネズミってご存知ですか?このネズミ、とても面白い行動をします。

Wikipediaによれば

「子育ての時には幼体や別の幼体や親の尾の基部を咥え、数珠繋ぎになって移動する」

そうです。

 一匹の子供が親の尻尾をくわえ、また別の一匹の子供が前の子供の尻尾をくわえ、、、という具合に、どんどん数珠繋ぎになって、親の後を付いて走っていくのです。非常にネストの深い関数を、私はジャコウネズミみたいだと表現したのです。

 関数の中で別の関数を呼び、呼ばれた関数の中で、また別の関数を呼ぶ…。それが15以上も連鎖するのです。これを情報隠蔽と言わずしてなんと言いましょうか!

そして、最後に呼ばれる関数は

 int f() {

  return 5; // 初期値を与える

 }

なんていう単純なコードだったりします。泣くに泣けないとはこういうことを言うのでしょう。

●SQL臭

 SQL文に機能の大半を移植してしまっていると、SQL文を読み解かないと、元々のプログラムの意図は分かりません。そして、そのSQL文がストアドプロシージャを呼び出しているだけだとしたら…。データベースに格納されている機能を追う羽目になります。

 SQLを直にコード中に記述してあるときなど、解読は至難の業です。それこそ、あっちこっちから、SQLが呼ばれて、その値によって状態遷移し、スレッドが起動され、データが更新される…。うーん。考えただけで、嫌気がさしますね。

 SQLを読むためだけに、専門家を連れてくる必要もありました。非常に複雑なSQLって、ある意味、神が降臨した、って思うくらい神々しいものですから(笑)。

●ゾンビ臭

 これは、ソースコードから臭うものではありません。精も根も尽きはてたエンジニアから漂うものです。

 膨大な労力をかけて、それでも報われずに、朽ち果てていくエンジニアたち…。朽ちてしまったエンジニアは、映画みたいに復活しません。そのまま朽ちているだけです。

■エンディング

 皆が疲弊していく中で、一人のエンジニアが言いました。

「魂を売って、悪魔と契約すれば、この地獄から脱出できるのかな?」

悪魔と聞いて思い出されるのは、

 マクスウェルの悪魔

です。これは熱力学上で考えられた架空の存在で、スコットランドの物理学者ジェームズ・クラーク・マクスウェルが提唱した思考上の産物です。

 熱は高いところから低いところに流れますが、その熱の流れを”自在に”操作し、熱を低いところから高いところに流す能力をもつ架空の存在として考え出したものらしいです。つまり、この悪魔は、エントロピーを逆転させることができる存在なのです。

 あれ? 今、我々がやろうとしていることって…。そう! エントロピーの逆転です。雑音(ソースコードの山)の中から情報(設計思想)を抜き出すということは、まさにエントロピーの逆転! 我々は、まさにそんなことをしようとしていたのです。

 雑音から情報を取り出す「情報サルベージ」! 悪魔に魂を売れば、もしかしたらできるかもしれない。いや、その前に廃人になる方が早いだろうか…。

 このプロジェクトの結末は、悪魔に魂を売ることなく、一人の勇者が上司に掛けあって幕を閉じました。

「俺たちをノイローゼにする気ですか!」

 勇者の一言が我々を地獄から救い出したのです。

 この話はここでおしまいです。今となっては、昔話ですけどね。

■教訓

”ソースコードにも、賞味期限がある”

 どんなに活きの良いソースコードでも、時間が経てば腐り始めます。腐るのを防ぐためには、防腐剤であるドキュメントをちゃんと書いておくか、他の防腐装置を用意しておいてあげましょう。

 そんなソースコードを食わされて、下痢をして体調を崩してしまう可哀想なエンジニアが出てしまう前に…。

Comment(5)

コメント

にゃん太郎

こんにちは。

 私も今担当しているソフトを引き継いだ(担当者はとっくに退社していて、メンテナン
スさえ行われていませんでした)時の事を思い出し、「あったあった」と思いながら読ま
せてもらいました。C++でしたが、「goto」がたくさんあったのを発見した時にはさすがに
少し思考停止しました(笑)。まぁ、私の場合は途中でくじけて実際の動作から仕様を作
り直しましたが、今でもいろんな呪縛に苦しんでいます。

Josiah

「for文で無限ループ」ですが、これにはれっきとした意味がありました。
(ただし for(;;) 限定)
その昔、まだ CPU の処理能力が低く、コンパイラの最適化の効率も悪かった時代、パフォーマンスを上げる術として、while(1) ではなく for(;;) で無限ループが使われていました。
なぜなら、while(1) ではループの先頭で必ず条件の評価が行われるのに対して、for(;;)では条件の評価をすることなくループするようにコンパイルされたからです。
その当時は、CPU の能力が現在の物とは比べものにならないくらい低い時代だったので、一回の条件評価にかかる時間は微々たるものでも、それが何千・何万と繰り返されると、処理速度に大きく影響しました。
そのため、無限ループに for(;;) を使用するなどの”技”を駆使して、少しでも処理速度を稼ぐ努力をしていたのです。

ハムレット

このコードが、最初から腐っていたのか、機能追加や改良を重ねて、腐って云ったのか、非常に興味深いところですね。

sourcemonitor等のソースコードのメトリックス計測ツールで計ってみると、面白いかもしれないですね。この手のツールは悪い方向の意味では測定値が現実を裏切ることはまず有りませんし。

虚数(i)

こんばんわ。にゃん太郎さん、Josiahさん、ハムレットさん。
コメントありがとうございます。

今回は、掲載の連絡がなく、いきなりコメント通知が送られてきたので、少々びっくりしてしまいました。

皆様のいろいろご苦労されているのですね。
この話は、随分昔の話ですが、いまだにドキュメントが存在せず、コードのみでメンテナンスしている製品が後を絶ちません。
どうしたもんでしょうかね。。。

コードを読むことで勉強になることは山ほどありますが、このケースでは疲労だけが残りました。
GOTO文が散乱するC++なんてのも、、、身の毛がよだちますね。
For文の無限ループの解説は勉強になりました。
今の若い人は、その深い意味を知ってから使っているのでしょうかねぇ・・・?

静的解析ツールを使ったこともあります。
当時、性能のよいツールはあまり無かったのですが、それでもいくつかを選んで試行しました。
ツールをセットして、一晩実行させた後、翌日会社に来たときに、ツールがアボートしていたのを発見したときは、驚きましたが。

I might be beating a dead horse, but thank you for potsing this!

コメントを投稿する