消えます消えます!  
 
 プログラムを作成するときに,さまざまな処理を行います。この時にやっかいなのは,いわゆる「落ちる」エラーです。落ちてしまうと,また最初からテストのやり直しになってしまいます。

 個人的な経験では,このようなエラーには「削除する」処理が絡んでいる場合が非常に多いです。
 ですから,ある機能を設計・実装している場合に,「このデータが削除される時に,残りのデータが正しくあるためには,現在の仕様・処理・操作手順で本当に大丈夫か?」を考慮すると,後々のテスト段階で非常に助かります。
 ここで重要なのは,「このデータは削除されないから,あんまり考えなくていいや。」と考えては絶対にいけません。なぜかというと「仕様は変わる」からです。
 少なくとも,私の働いている部署の場合は,仕様と納期は必ず悪いほうに変わります。

Tipsに戻る


 餅は餅屋 デバッグはデバッグモード  
 
 Visual C++では,ビルドの時に「Debug ビルド」と「Release ビルド」を選択できるようになっています。
 Visual Basicでも,Ver.4以降では,コンパイル定数をサポートしますので,同様の手法を取ることができます。
 MFCやVBのランタイムは,DebugとReleaseでとんでもなくサイズが違いますが,これは「Debug時だけ組み込まれる自己診断用のコードが大量に入っている」からです。
 例えば,

  1. 正常な処理中では,実際にはNULLポインタ(VBならNothing)が渡されることはないが,NULLポインタ(Nothing)が渡されたかどうかをチェックするようにする。
  2. 内部で使用する連結用バッファの長さは,実用上十分な128バイトだけ確保しているが,それを越える領域が必要な入力データかどうかチェックするようにする。
  3. 処理の最適化を行っているが,それに伴う副作用がないことを,正規の計算結果と突き合わせることでチェックする。
 といったことを行います。これをやっておくと何が助かるかというと,モジュールを結合してからエラーが出てきた場合に,「俺は悪くない!」と言うのが言いやすい事です。「エラーがあったら,どこそこのチェック処理で引っかかって,ログが出てくるはずやけど出てきてない。だから他のどこかが悪い!」という事です。
 もうひとつは,「自分でやったテストでは見つからなかったバグが,他の人の環境で出てきた場合には,その場でデバッグできる。」ということです。自己診断に失敗するとダイアログを出すようになっていると,メンバーの誰かが,親切にも「変なダイアログが出てきた」と報告してくれます。メンバーが,自分のデバッグを助けてくれるのです。
 Windows95やNTは,デバッガの後付ができますので,VCやVB5の場合は,そこでたまにしか発生しないバグが出てしまった場合も,運が良ければ原因を突き止めることができます。運が悪くても,実際にその原因によって障害が引き起こされる前に,デバッグを行うための材料を,人間が探さなくてもコンピューターが見つけてくれます

 とはいうものの,私がよくやってしまうミスに,整合性をチェックする方式がバグっているために,「正常に動作をしたときだけ警告される」ようなコードを入れてしまう場合があります。みなさんも気をつけましょう。

 そしてこういった自己診断チェック用コード部分は,リリースモード時のコンパイルではコンパイル対象にならないようにしておくと,自己診断によるパフォーマンスの劣化やサイズ増大は無視できるようになります。

Tipsに戻る


 自分の身は自分で守れ  
 
 これはどういうことかというと,「パラメータとしては有効だが,処理上は問題があるパラメータ」で呼び出された場合にも,できるだけエラーで戻るようにするということです。
 Window95やNT上での場合,ひどい場合には悪名高い一般保護違反すら検出して,リカバリー処理を行うことが可能です。
 よくあるパターンとしては,

  1. コードを受け取る場合は,有効範囲以外が渡された場合に失敗させる。
  2. ポインタを受け取る場合は,NULLポインタをチェックする。
  3. 可能なら,本当に自分が意図している形式のデータかどうかをチェックする。
 このようにしておくと,自分自身の動作が不安定になる要素を減らすことができます。また,バグによる副作用で,他のモジュールに影響を与えることも少なくなります。なにより,実際の障害発生部分を,他のモジュールに押しつけることができます。
 受け取ることのできる全てのパラメータ(の組み合わせ)に対して,定義された処理結果(や副作用)を用意してください。

 あるプロジェクトで過去に私が手がけたモジュールには,エラーコードとして,「アドレス違反」を用意してました。あらかじめ,指定された領域に対して実際に書き込みが可能かどうか試し書きをしてみて,その時点で失敗したらこのエラーで呼び出し元に戻るようにしていました。
 ただしこの手のエラーは,大本の原因は呼び出し元にあることが多いので,やりすぎると,変に呼び出し元が正常なエラー処理をしてしまい,バグを見えにくくしてしまう事があります。そのプロジェクトの時もそのような場合がありました。何事も程々が肝心だと反省してます。
 
Tipsに戻る


 インターフェースをあいまいにしない  
 
 別の言い方をすると,「結合テスト」でのテスト範囲になるような部分で,曖昧な部分を残さないということになります。具体的な例でいうと,

  1. コードを渡すときには,コードのパラメータを数値型ではなくenum型で宣言する。
  2. 不定形のデータを渡す必要がある場合には,VOID*(VBならObject型やAny型)ではなく,データをクラス化して,それらの基本クラス(VBなら,特定のObject型)を渡すようにする。
 といったことが挙げられます。このようなことをすることによって,どういう効果が得られるかというと,「呼び出し側で変な呼び出し方をしようとした場合に,コンパイル時にエラーにされてしまう」という点です。人間ではなく,コンパイラがエラーになりそうな部分を見つけてくれます。
 つまり,「言語の構文を満たし,かつ不正な値を受け取る」可能性を減少させます。

Tipsに戻る


 インターフェースをあいまいにする  
 
 上と言ってることが違うって? 確かに違います。違うからには違う理由があって,何が違うかというと仕様の確定とPGのタイミングが違うんです。
 通常の場合は,仕様の確定のあとにPGが行われます。この限りにおいては,これから述べることは当てはまりません。
 しかし,「仕様が全て確定していないが,確定している範囲でプログラムを書かなければいけない状態」になった場合には,「インターフェースをあいまいな形式にして,多少の変な仕様になった場合でも,インターフェースに投げるパラメータを調整することで対応」するようにできると効果的です。
 これは,いわゆる「呼び出される側をコンパイルし直したら,呼び出し側もコンパイルし直さなければならない」状態を避けるようにするということです。
 当然,確定したときに変更あるいは追加になった機能を,呼び出し側が必要としているなら,呼び出し側の変更は当然必要になるんですが,そうでない場合は,変更しないですむようにしておくと,呼び出し側の担当者から喜ばれます。

Tipsに戻る


 人のものは俺のもの 俺のものは人のもの  
 
 これは,「自分で書いたプログラムは,できるだけ人からも(さらに可能なら,その機能単体で)利用されることを考慮して書く」そして「既にあるものはわざわざ作らない」という事です。要は,総「共通ライブラリ化」ですな。
 前半部分に関しては,利用されることを考慮して書くだけで終わってしまうと,もちろん余計にしんどいだけです。
 しかし,ひとたび「ここの画面を共通化して…。」「ここのアクセスを共通化して…」となった場合は,状況は一変します。
 あなたは,「これはここ,これはこのモジュールをリンクして,これとこれとこれを呼べばデータが取れるから,後はがんばって。」あるいは,「それは,このコントロールを張り付けて,そのプロパティを見たら選択されたやつがわかるから。」そう言ってあなたは家に帰ればいいんです。きっと,あなたが寝ている間も,その人は共通化する部分の切り出しと,それに伴うバグ退治で一生懸命になっていることでしょう。

 もう一つ効果的な場合は,それぞれで同じ事を独自の方式でやっていて,それでは具合が悪いから,統合しようという話になった場合です。一般的に,利用されることを考慮してあるモジュールの方が,汎用性・拡張性が高いことが多いようです。当然の事ながら統合の際の労力が最小になる方式で統合することが大半だと思います。従って,「他からの移行が行いやすい方式」にしておくと,「自分のやっている方式に合わせてもらう(自分の担当分を変更しなくて済む)」可能性が高くなります。

 後半部分については,これは私も良くやってしまうことなんですが,「既にあるやつを改めて作りなおす」事についてです。機能的に不足がある部分はもちろんしょうがないです。しかしその場合でも,できるなら「作り直し」たり「書き換え」たりしないで,「付け足す」方向では実現できないのかを考えてみてください。
 大体にして,「作った」からには,動作テストをしなければいけません。既に誰かが作っていて,そこそこ動くやつがあるのに,わざわざ1から作ってしまったら,そこの部分のしんどいテストをするのは「あなた」です。バグの対処をするのも「あなた」です。
 「付け足す」あとにバグが出ても,それが付け足した部分が原因でないのなら,バグの対応は,「付け足し元」がやってくれるじゃないですが!テスト項目のピックアップは,「付け足す」部分だけで,肝心の部分は,「付け足し元」がやってくれるじゃないですか。どうしてわざわざ「あなた」が苦労する必要がありますか? それにこのような場合,「元を作った人がした失敗」を,そのままあなたもしてしまう場合が多いようです。わざわざ人が先に失敗してくれていて,しかもそれに対する解決策もとってもらっているのに,あらためて「あなたが」失敗する必要どこにあるのでしょう?
 人の書いたものの仕様を新しく覚えるのは大変そうに感じます。また,納期直前のような場合は,とりあえず作って対処する必要があるのも確かです。でもよく考えてください。仕様を把握する(覚える必要はありません。ヘルプでも何でも見ながらできればいいんです。)間に,あなたはいくつのバグを潰せますか?

Tipsに戻る


 マルチスレッドは便利!  
 
 システム開発が仮運用段階になると,パフォーマンス上の問題が発生してきます。
 代表的な物には,ありとあらゆるところで使用される一覧表示を行う部分があります。
 これはそもそも,テスト段階で少量のテストデータしか用意していない(=テスト期間が少ない)ということと,プログラム上でも,必要なところで大量のデータを処理する場合の考慮がされていない(というか,ほとんどの場合する余裕がないのでしょうが)事によります。
 このような場合は,可能であればマルチスレッド化を行うと,特にあなたがユーザーインターフェース周りを手がけているのであれば,パフォーマンス上の問題について文句を言われる可能性は激減します。
 一歩譲っても,1データ要素の処理毎に,ユーザーの中断操作などを受け付けられるようにできればより効果的です。
 実際には100件0.5秒でデータを取ってくる必要がある場合でも,大抵の場合で欲しいデータが先頭20件以内にある場合なら20件0.5秒で処理できればよくなります。そしてこれは(データベースなどの性能を無視すれば),データ件数が1000件だろうと10000件だろうと,大抵の場合に0.5秒でユーザーに反応を返すことができるということです。体感上の速度を上げられるので,ごまかしができるんですね。

 もちろんマルチスレッド化する場合は,同期処理の問題があり,この部分をどうするかが最大の課題ですが,それさえ適切に処理できれば問題はないのです。
 新たに考慮しなければいけない点は,極論で言ってしまえば,サーバー上で共有しているファイルや,同一端末に複数のサブシステムが起動した場合と何ら変わることはないです。全てが共有メモリになってしまうというのはありますが,処理上の考慮という点では,実現方式こそ違え,マルチスレッド化したからといって,特別な注意は必要ないはずです。
 それとも,あなたはそんないい加減な,複数の端末から何らかのタイミングで書き込みが競合した場合に動きがおかしくなるような,怪しい処理を今までずっとやっていたのですか?

Tipsに戻る


 3種の神器  
 
 私が所属してる部署の場合,少なくとも今までは,サブシステム単位で開発範囲を分割しています。そこから実際に開発していくのですが,サブシステムという単位では分割できない機能がいくつか出てきます。
 大抵は,それらを集めて「共通ライブラリ」と称して開発を行いますが,いつも決まってそれらの中から漏れる機能があります。これは,次のような理由によります。

 で,毎回これに該当する機能で,テスト・運用効率を下げている機能が,  の3つです。
 実際にはこれらの機能は,最終的には実装されているんですが,実際にどの程度の密度でアプリケーション本体側から使われているかというと,余り密度は高く使われていません。
 これはまず1つに,私の所属している部署での開発の場合は,これらの機能が実装されるのが,後半になって行われると言うことが挙げられます。ここで,インターフェースだけでも早期に決定されていれば,ダミーを埋め込むなり何なりができるのですが,一通りプログラムが組み上がった後で1から埋め込み直すのは,なかなか嫌になります。(特に,後半になると納期との絡みで切羽詰まっている場合が多いのでなおさらです。)
 もう一つは,機能を使用する時点では満たすことのできない前提条件を,機能が動作するために要求される事が挙げられます。
 私の部署で実際にあったのは,このような機能が「システムにログインしていないと使えない」場合です。実際にここで挙げているような機能を使う際には,ログインしていない場合でも使用される場合があり得るのは明白です。このような場合にどうするかの答えは,ライブラリ開発側にも,アプリケーション開発側にもありません。そのような前提をたてていないからです。
 このようになってしまうと,なまじ「方式は統一する」あまりに,「できないからやらない,やらなくていい」という口実を与えてしまいます。こうして,数々の規定が有名無実化していきます。

 つまるところ,システムのアーキテクチャやポリシーの設計・伝達ができていない事から来ると(私は)思ってるんですが。

 もちろん,実際にそのレベルからできるかというのはまた別の問題です。
 でも,それができないならできないでいいから,できない事を前提としたスケジューリングなり機能範囲の決定をしてほしいし,そうでないスケジュールに沿ってできなかったことをSEやプログラマのせいにしないでほしいなぁ…
 そのあたりのさじ加減をどうするかの決定と意志伝達は,完全にプロジェクトマネジメントレベルの話だと思うんですが。

Tipsに戻る


 例外のない規則はない  
 
 特にVisual Basicの場合は,組み込みの機能で発生するエラー処理はほとんど全て例外処理にされてしまいます。例外処理という名前に似合わず,例外処理は必ず必要です。
 それがいやなら,Visual Basicは使わずに,Visual C++などの他の言語を使用しましょう。今まで通りに黙って怪しい処理を行ってくれます。
 使用するファイルは必ず無い物として,接続先のデータベースや,その中のテーブルは必ず無い物として,渡されたメモリ領域は必ず確保されていない物として,渡されたコードは範囲外の値だということを心してください。

 …とはいうものの,実際の所,私はとてもここまでできません。きりがないです。だから私はC(++)言語の方が好きなんですよ。運が良ければ例外を黙って見逃してくれますから。

Tipsに戻る