げんこうといっしょ

2009/03/22

2月半ばほどから微妙に忙しかったのだが、3月に入ってからは本格的に苦しいことになってしまった。「現行システムと同じことをするプログラム」なる物を作っていたのだが、その現行システムというやつが何をどうしてそういう結果になるのかが理解できない酷い代物であるため、開発に苦労したのだ。

たとえ既存部分のプログラムが見苦しい代物であったとしても、業務処理がはっきりとしていれば、「現行システムと同じ処理をするプログラム」と作ること自体は不可能ではない。なぜならば、既存部分を全く無視して業務用件から仕様を導き出せばいいのだから。しかし、そもそもの業務用件が複雑怪奇な場合はそうもいかなくなる。

なぜならば、業務処理内容が複雑だと、その仕様を理解している人間が存在しなくなってしまうのだ。つまり、客に「この処理はどうすればいいのか」と聞いても「よくわからないから、それは現行と同じようにしておくように」と言われるだけで、何をどうすればいいのかについて教えてもらえなくなるのだ。

いわゆるレガシーという奴である。

しかし、どうしてそういうことになるのだろうか? そもそも、業務システムという奴は、元々人間が手でやっていた仕事を機械でやらせるようにしただけの代物のはずである。ということは、もとはそれほど複雑な物ではなかったはずなのである。それなのになぜ、奇っ怪な化け物になってしまうのだろうか?

いろいろ理由はあるのだろうが、俺が思うに、システムというのは使われ出すと、仕様の元となった業務自体に影響を与えるからなのだと思う。つまり、業務を元にシステムの仕様が決められたはずなのに、システムが逆に業務に影響を与えるのではないかと思うのだ。(というか、そういう話をどこかで聞いたことがある気がする)

システムを使う側はシステムの中身については一切知らない。ただ、入力と出力が見えるだけである。そして、とにかくそれを使って仕事をしなければならない。そうなると、システムにあわせて人間が業務処理を変えてしまう可能性が出てくる、というか確実にそうなる。

そして、そうなると今度は、人間が業務処理を変えたのにあわせてシステム側が改修される。改修されたらまたそれに合わせて人間が仕事のやり方を変える・・・。そういったことの繰り返しで、長年の間に誰にも仕様が把握できない怪物が育ってゆくのである。

そうなるとその怪物には誰にも手が出せなくなる。手が出せないからそのまま放置しておく。するとますます怪物が成長する・・・。悪循環の繰り返しである。

おまけに、その悪循環を断ち切るべくシステムを作り直したとしても、システムを作るときには決してビジネスフローが見直されることがないため、新規にできた物は結局現行と同じ複雑怪奇な怪物だったりするためたちが悪い。

おそらく、「できた物」が「元となった仕様」に影響を与えるという問題は、元の仕様が人間そのものである限り、決して根本的には解決できない問題なのだろう。しかし、だとしても設計書やその他の文書として「どんな処理を行っているのか」について、ちゃんと書いておけばこんな苦労はしなくて済むはずである。なぜ、そんな当たり前のことができ無いのだろうか?

不思議だ。

事例

2009/03/02

世に言われている通り、業務システムで作られるプログラムには、時に常軌を逸して酷いものがある。正直言って、どこの素人が書いたのかと言いたくなるほどいい加減なものもある。

例えば、メモリを解放し忘れてたり、エラーが発生するとメモリの二重解放が起きてプロセスが落ちたり、ハンドルを閉じ忘れていたりする。だがまぁ、それはまだ可愛い方だ。とりあえず、結果オーライで動いているのだし、それに、間違えやすいポイントであり、必ずしも理解できないわけではない。

だが、中には作った奴の正気を疑いたくなるような記述もある。例えば、次のようなプログラム。

/* ↑ファイル名を取得する */
r = GetPrivateProfileString( ・・・ );


今時GetPrivateProfileStringを使うなと言う突っ込みではない。また、「ファイル名を取得する」等という、明々白々ことをコメントに書くなと言う突っ込みでもない。俺が怒っているのは、明らかに矢印の向きが間違っているということなのだ。

一体これは何を意図した記述なのだろうか? これを書いた奴は矢印の向きを間違って覚えているのだろうか? それとも、正真正銘の気違いなのだろうか?



/***************************************************
* 日付をする処理
***************************************************/
int ProcessDate( ・・・ )
{
 ・・・
}


ユーザが入力した日付の入力チェックを行う関数の前についていたコメント。「日付をする」って日本語として成立してネェし。


/* ↑日付のニューメリックチェック */
int d = atoi( strDate );
if ( d < 101 || d > 1231 ) {
/* ↑エラー */
・・・
}


atoiで文字列を数値に変換してもニューメリックチェックにはならないと思うのだが、気のせいだろうか? 値の範囲のチェックぐらいはできるだろうが、その間にある不正な値、例えば「0231」とかのチェックはできてないし。

ついでに言うと、この無意味な処理の後で、上記の「日付をする処理」を呼び出して、詳細な入力チェックを行っていた。


long wlong1[1000];
long wlong2[1000];
long wlong3[1000];
char *area;
char *area2;


データの並べ替えを行うための作業用の領域らしいのだが、変数名からはそういったことが読み取れない。変数名がwlongでは、long型の作業用領域だと言うことしか判らない。変数名で言うべき事は値の型ではなく、それが何のために使用されるのか、何の値が格納されるのかということを言うべきである。

当然、同じ事がareaにも言える。areaでは、何が入るのか全く想像もつかない。

また、long型配列に格納された整数値を並べ替える処理を自分で実装するな。それも、なにやら奇妙でトンチンカンなソート処理を作っているんじゃない。qsort関数ぐらい使え。というか、そんな暇があったのなら、コメントをちゃんと書け。


/***************************************************
* ファイルを検索する処理
***************************************************/
int MakeEnv( ・・・ )
{

}


「MakeEnv」という関数では、不要になったファイルを削除する処理を行っている。にもかかわらず、コメントには「ファイルを検索する処理」。何も言うことはない。


「前向き」に解釈すれば、作っているときにテンパッてこうなってしまったのだと、考えられなくもないが、しかし、余りにも酷いのではないだろうか? 常軌を逸したコメントを見ていると、本当に書いた奴の知能のレベルが低かったのだと思わざるを得ない。