プログラム悪戦苦闘日記

はてなダイアリーからの移行(遺物)

糞プログラムをやっつけろ!

 昔も似たようなことを書いた気がするが、あれから少しは進展があったので、再びまとめてみようと思います。いわゆるシステム開発なんて仕事は、Excelいじっているか、他人の書いたプログラムを見てるかどっちかですからね^ ^;

ヘタレプログラムとは? -ヘタレプログラムの定義-

 ヘタレプログラムとはどんなプログラムか? まぁ、ひとことで言えば「きたないプログラム」なのだが、きれい/きたないでは主観が入るとはなんとか言う人もいそうなので、定義を考えてみました。いちよう断っておきますが、これは私が勝手に考えたものであり、また今後調査/分析が進むと定義は変わる可能性はあります、というより間違えなく変わります。
 ヘタレプログラムとは、以下のすべてを満たすプログラムとしよう。

  • ドキュメントがない。あるが最新のプログラムと同期がとれていない。同期がとれていても、擬似コードを日本語にしただけ。
  • 回帰テストができない。単体テストに何やったかわからない。
  • まだ実可動していない。実可動しているがバグだらけでプログラムがFIXしていない。

 まぁ、ヘタレプログラムである条件は、これらの内1つ、としてもあまり変わらないだろうが(真にヘタレは、どれか1つだけ満たす、なんて器用なことはできないから)。

 ソフトウェア業の定石には、『実可動しているプログラムを、スクラッチから書き直すな』というのがある。プログラムは汚くても、それはなにかのバグ対応で付け足したものだから、また始めから書き直すと同じバグを入れ込んでしまうから、という理由である。しかし、だ。ヘタレプログラムは本当に汚いのだ。どうしようもなくダメなのだ。はっきりいって、プログラムが得意な高校生に、時給2000円くらいでプログラム書かせたほうが、はるかに良いできのプログラムができると思えるほどダメダメなのだ。こんなプログラム書くやつに月数十万払ってるなんて… なんて太っ腹w。とにかく、この定石ですら破ったほうが良いのでは、と真剣にプロジェクトの方針を考えなくてはならない程の、ヘタレっぷりなのである。

リファクタリングが効かない訳

 昔も書いた気がするので、繰り返しになってしまうかもしれないが、自分でも何書いたか覚えてないので気にしないことにする。リファクタリングが出来ない理由だが、まず、リファクタリングが出来る条件として

  • バグがない。少なくても現段階で認識しているバグは無い。
  • 単体テストケースがある

 の2つは必要であろう。最悪単体テストケースが無くてもドキュメントがあれば、テストケースは作れそうである。しかし、ヘタレプログラムはその定義から、

  • そもそもバグありで、リファクタリングできない。
  • せめて単体テストケースでもあればよいが、回帰テストができないので無理。
  • じゃぁ、ドキュメントからテストケースを作ろうと思っても、最新ドキュメントがない!

という、ヘタレ・スパイラルでどうにも身動きがとれないのだ。じゃぁ、ヘタレプログラムをどう扱うかというのは、結局プログラムを見て仕様を起こすしかない、というどうしようもない結論に到達するわけである。

ヘタレプログラムの傾向と対策

 ここが一番のメインであろうか。ヘタレプログラムは、書いた人間(?)によって汚さ、ヘタレ具合が違う、個性がはっきり出てくるものなので、なかなか体系的に扱えないのだが、それでも最低限の共通点と対策方法は考えました。

1.グローバル変数てんこもり
 今も昔も忌み嫌われているコレ。ちゃんとしたプログラマなら自然と避けるこのグローバル変数は、ダメなパンチャー… じゃなかった、プログラマほど大量に使う。C言語で、関数の引数がない関数が多い場合は、ほぼこのグローバル変数病に取り付かれていたプログラム --- 平たく言えばハズレを引かされたと思って間違えないだろう。ちなみに、Javaにはグローバル変数がないから関係ない話、と思ってないだろうか。どんな言語だってグローバル変数はあります。言語レベルで無いようにしても、この手のプログラマグローバル変数を捏造します。もっと別のところに労力を使って欲しいんだが。Javaグローバル変数を知りたいですか? きっとあなたの向かいの自称プログラマが書いているソレだと思うのですが。ほら、そのフィールド変数です。え?ちゃんとprivateにしているから、カプセル化を守っているだって? 自分の汚いプログラムをカプセル化(=見せない)してるだけだと思うのだが、そいつのプログラムを良く見てみなさい。クラスが1つしかなくて、そのクラスが2000行くらいあるでしょ。C言語だって main に2000行書くヤツはたくさんいたんだから、Javaだって main か execute か doGet か知らないけど、この関数(もうメソッドとは呼べないでしょ)に2000行書くヤツがいてもおかしくないでしょ。こんなクラスのフィールド変数は、すでにグローバル変数と同じであることの気づいてください。あー、わかってるって。あんたの書いたプログラムは、クラスが1つじゃなくて3つあるって言いたいんでしょ。でもそのうちのひとつはこんなかな

    DameDame dd = new DameDame();
    dd.check();
    String data = dd.getData();

ここで、check()メソッドの前にgetData()メソッドを呼ぶと動作が未定義になるという、ローカルルールが存在している。もちろんこんなことはコメントには書いてないし、ましてやドキュメントなんかアリマセン。
 余談が長くなったけど、グローバル変数は一番使われながら、最も対策がとりにくいモノです。こいつの対策方法は… 残念ながらありません。あきらめるしかありません。ひとつひとつの変数について、どこで値が代入されているかを追っていくしかありません。どういつグローバル変数なのに、関数によって役割が違ったり、ループカウンタをグローバル変数にするヤツもいるので気をつけましょう。普段グローバル変数なんてあまり使わないでしょうから、この変数の奇妙な働き/使われ方には、罠が大量に仕掛けられていることでしょう。

2.コメントがない。コメントが意味不明。コメントにウソがある。
 昔も書いたと思いますが、コメントは信用しないように。いきなり Step3:○○処理 というコメントを見ても、Step1,2がどこにあるんだ、とかStep3についてのドキュメントはどこにあるんだ、とか疑問に思ってはいけません。無視に限ります。

3.ふか〜いifネスト
 もういいですよね。これと姉妹品にifとelseがすご〜く長い、というのがあります。これの対応方法は、ifとelseの対応を慎重に調べていくことです。

ヘタレプログラムの解析方法

 長文で疲れてきました。さて、ヘタレプログラムの解読方法ですが、これの解析方法に従来のUML(クラス図とかシーケンス図とか)やフローチャートは無力です。私はすべて試しました。でもうまくかけません。なぜか? それは

  • ヘタレプログラムはデータ中心に考える

 ここでいうデータは変数とほぼ同義ととってよいです。結局ヘタレプログラムがヘタレである要因は、どこでどの変数に何の値が代入されているのかが、芸術的に分からないことである。だから、変数中心に解析していくのが近道なります。
 このとき、解析の妨げになるのが if 分岐です。変数の状態よって、どこの処理が実行されるのかが致命的に分からなくなる。しかし、この if 分岐にもパターンがあることが分かりました。それは

  • if条件文のなかで、いつも見かけるアノ変数をチェックする

 結局、解析している1本のプログラムはなんかの業務1処理な訳ですから、こんなに大量な分岐を発生させるには、いつもお決まりの変数で分岐させるしかありません。それが「買い/売り」みたいな売買区分だったり、「午前/午後」みたいな時間区分だったり、「登録/削除」みたいな処理区分だったりするわけです。ヘタレプログラムは、同じ条件分岐をいろいろな関数で何度も何度も行うのです。だから、いち早く if分岐のキモ変数が何かを発見するのが、解読への近道になります。ちなみにこの変数は、関数によって名前を変えたりしているのが普通ですので気をつけましょう。
 さて、変数の動きが把握できたら、関数ごとのI/O表を作っておいたほうがいいでしょう。なにが入力の変数で、なにが出力なのか。もちろんグローバル変数も含めて、です。これがあると今後の作業がかなりラクになります。
 さて、変数の動きの次は

  • フローを解析する

 です。フローをまとめるには何図がいいのでしょうか。残念ですが、これはまだ研究中です… ただ、いまのところはシーケンス図がまぁまぁ良いです。ただ、このシーケンス図には if 分岐も書いておきましょう。ヘタレプログラムは if 分岐がキモですから。

最後に余談。なぜ if pi---ネストが生まれるのか?

 これ、疲れたのでまた後日書きます。