【質問#167】既存のソースコードの調査手順について

質問・悩み相談の回答です。

質問

はじめまして。
先日このブログにたどり着いてから過去ログを遡りながら楽しく拝見しています。
ちなみに一番好きな記事は「開発マシンの人権スペックについては俺にも語らせろ」です。

さて、早速ですが質問したいことがあります。
リファクタや不具合調査、新規機能追加などを、自分が作ってない既存のソースコードに対して行うことになるのはプログラマとしては避けられない運命かと思います。
そのソースコードを調査する際にwithpopさんはどのような手順で調査するのでしょうか?

私は、例えばリファクタであればわかる範囲から手を付けつつ中身を理解し、さらに手を広げるという手順になることが多いです。
「読むだけより手を動かしたほうが理解できる」これは一つの真実だとは思います。反面、「対応する範囲全体を理解し、広い視野でどのような最終形にするか設計してからコーディングすべきでは?」とも思います。

自分よりも広い知識・高い技術を持った方であればどのような視点や考え方で取り組むのか、伺ってみたいと思い質問いたしました。
お時間がとれましたらご回答いただけると助かります。

回答

いやー、なんかいろんな記憶が蘇ってきましたね。

私が新卒で初めて入社した時に見たコードはC, C++, FORTRAN, 某社製独自言語の4種類でした。FORTRANとCを読むことが多かったかな…。

原子力業界でした。原子力では基本的にすべてCで実装されてました。データベースも通信も何もかも。OSSは使ってはいけないという方針でした。これはすごかったですよ、POSIX API以外は使ってはいけないんです。要するに自分たちのコントロール範囲外のコードは動かしたくなかったんでしょうね。これは厳密に定まっており、よくある話ではあると思いますが、サクラエディタとかTeraPadなどのよくあるフリーソフトなんかも当然使用禁止でした。そんな環境で数万点のセンサーデータを毎秒収集して演算を行い、プラント全体と炉心の制御をするわけですから、非常に重厚なシステムでしたね。全体を説明できる人は多分居ないんじゃないかと思います。

しかしながら、なぜかOSはLinuxでしたね。OSSの塊の上でOSSの塊に依存して動く自社製ソフトでOSS非依存を目指したところで何の意味があるのかよく分かりませんでした。まあいいです。

更に凄いのが最上位に位置する仕様書は操作マニュアルだったということですね。客先に渡す操作マニュアルに書いてあることが全てで、そこから仕様が決まるという不思議な世界でした。なんでそんな文化になったのかよく分からんね。

そういうわけで基本設計書、詳細設計書もこれもよくあるとおり、とりあえずフォーマットを埋めるみたいな感じの内容で中で何をやっているか説明しようという気概が全く感じられない文章になってました。そればかりか日本語がおかしい文章が数多あり、日本の有名な大学を出た人たちが集まって作ってきたソフトがこれか…とひどくがっかりしましたね。でも原子力の仕事自体は面白かったです。普通のプログラマ(志望)の人なら即辞めてたと思いますが、私はそれでも楽しかったです。

で、そのコードを修正しないといけないんですよね。いやーしんどいですよ。まずバージョン管理システムが「Windowsファイル共有で作られたフォルダを読み書きする権限がある人を1人に集約する」という方法でした。しかもこれアクセス権限を細かく設定してたのではなくて、「運用でそう決めてる」だけなので、たまに「間違って書き換えちゃいました」みたいなのも出てましたね。しんどい。あと変更履歴は全部コメントで残せって感じです。

しかもそのコードのヘッダに更新履歴みたいなのが日付付きで書かれているんですが、最初の日付が1980年でしたからね。私が生まれたのが85年、入社が09年でした。まさか生まれる前に書いたコードを修正するとは思わなかった。

さらに辛いのが、これ実際に動いている状態をデバッグするのが非常に難しいんですよ。なぜならば数多ある総数100個くらいコードにして100万行以上みたいな巨大なプログラムが全部一体になって動かないと動作しないから。だから自分の通信相手(これも生のソケットでやる、HTTPサーバとかは絶対に使えない)のモックをそれぞれが作って「多分動くだろう」みたいな状態にしておくという感じですね。そんで組み合わせ試験まで持っていってバグが見つかると書類を3つも4つも作ってそれぞれに6人位のハンコを貰わないといけないという運用。しんどいよねー。

ちなみにエディタはviだけでした。vimじゃなくてviです。しかも、LinuxじゃなくてSunOSとかで動いている場合もありましたね。Solarisになる前ですね。Solarisですら殆どの人は「なにそれ?」って思うかも知れませんけど、それ以前でした。その時代に付属していたviなんてきっついですよー。ダイヤモンドカーソルも動かなかったな…。

で、そういう非常に開発も運用も社内手続きも地獄みたいな環境でかれこれ4〜5年?くらい働いたわけです。

だいぶおっさんくさい話になってしまいましたが、ともかくその経験をもって断言できるのは、

「読むだけより手を動かしたほうが理解できる」これは一つの真実だとは思います。反面、「対応する範囲全体を理解し、広い視野でどのような最終形にするか設計してからコーディングすべきでは?」とも思います。

間違いなく後者です。

場合によって前者を選ぶこともあります。でもそれは緊急的な対応を求められた場合くらいです。

クソコードはクソだからリファクタリングなりバグ修正なりが必要になるのであって、これ場当たり的な修正ではまた将来的に新たなバグを生み出し、最善でも保守に死ぬほどコストがかかるという状態に落ち着きます。これを解決するには、最初に大きなコストをかけてあるべき姿を定め、その状態になるようすべてコーディングし直すことです。

そのためには、「デバッガで動作中のコードの変数に何が入ってるか覗く」みたいなことに頼り切ってしまうと、何となくわかった気になってしまうので危険です。具体的には「Aという変数にBという値が入ってるからこれを元にCを導き出そう」みたいな結論を早々に導き出してしまうのです。人間は面倒くさがりなので。でも、もしかしたらAという変数にBという値は入っていない場合もあるかもしれません。それはコードを追わない限り決して分かりません。だから最終的にはコードを目で追う必要があります。

読み取ったコードは紙に書いて図示します。図で書かないとわからなくなります。アナログな方法ですがこれがスパゲッティクソコードを理解するには最も適切と信じています。

たいてい、クソコードは設計も何もなくいきなり書き始めてああでもないこうでもないと試行錯誤するうちにif文が5つも6つもネストしたり似たような名前の関数が何個も定義されたり、一つの関数の中身が1000行になったりします。原因は明らかで、ちゃんと設計してないからです。コーディングという作業は設計が8〜9割でコーディングが1割くらいです。数行書くのに1〜2時間かかることもあるのが本来のコーディングで、1時間で100行も200行も書いたらそれは設計をすっ飛ばして試行錯誤しながら書いてるのに等しいと私は思ってます。(ここに挙げた数字は感覚値でかいてるのであまりあてにしないでください)

まあ、今回のご質問の対象になっているコードがクソコードでない可能性もありますが、リファクタリングが必要と書いているのでおそらくどちらかというとクソコードなのでしょう。リファクタリングが必要というのはつまりそういうことですので。

というわけで、他にも色々違う意見のお持ちの方もいるとは思いますが、個人的には「既存のコードを修正したりリファクタリングするならば、全部のコードをしっかり読んで仕様を抽出し、設計からやり直すしか無い」と思います。何か楽できる銀の弾丸的な方法は無いですね。

以上、簡単な回答になりますが参考になりましたら幸いです。