バグったプログラムは即座に殺せ
1. 原則
バグったプログラムに処理を継続させてはならない
- プログラムの実行中にエラーが発生した場合は速やかに処理を中断しなくてはならない
- エラーが発生しているにも関わらず、処理を進めるようなコードを書いてはいけない
- 例えば、↓の例ではループ中でエラー(例外)が発生してもエラーを握り潰してループを継続しているが、このようなコードを書いてはならない
def main():
data_list = get_data_list()
for d in data_list:
try:
# 何かの処理
# ...
except:
pass # <-- エラーを握り潰してループを継続しようとしている
2. 根拠
デバッグが困難になる
- エラーを隠蔽して処理を進めた場合、後続処理や処理結果に異常が現れることになる
- 実際にエラーが発生した場所(処理)と発覚した場所(処理)が離れれば離れるほど原因の分析が困難になる
- 隠蔽されたエラーが原因で後続処理で更にエラーが発生した場合、真のエラー原因を突き止めることが困難になる
- エラーの再現も困難になる
処理結果の信頼性が低下する
- エラーが発生した状態で処理を継続した場合、その処理結果は最早信用することができない
- 本来なら異常として扱われるべきデータを無理に処理を継続している為、出力される処理結果も異常なものになるのは当然である
- 異常な処理結果が異常なものと気付かれないまま、後続処理やユーザーに提供されてしまうと被害範囲が拡大してしまう
3. 指針
エラーを検知した時点で処理を中断する
- 例外が使える言語なら例外を発生させる
- 例外機構を持たない言語でもプログラムをエラー終了させる
事前条件/事後条件を満たさない場合もエラーとして扱う
- 処理の事前条件を満たさない場合(必須パラメータ不足など)はエラーとして扱う(適当な値を補填して処理するようなことはしない)
- APIやメソッドが事後条件を満たさない場合(返却値の形式が仕様を満たせないなど)もエラーを返し、不正な結果がクライアント側やユーザーに渡されないようにする
4. 注意事項
エラーの発生を織り込み済みで設計することもある
- バッチ処理など大量のデータを一括で処理する場合、「不正なデータは処理をスキップする」ことを正式な仕様とすることがよくある
- 当然だがそのような場合はプログラムを停止させる必要はない
5. 関連パターン
参考資料
- センス・オブ・プログラミング!(前橋和弥) 3-6-4 バグのあるプログラムには生きる価値はない
Appendix-1 事例集
事例1: 欠落情報の補填
- 必須パラメータが欠落しているなど、事前条件を満たさない入力に対し、プログラム側で適当な初期値を設定し処理を実行していた
- 当然、正しい処理結果は得られず、処理結果がおかしいというクレームがユーザーから寄せられた
- そもそも、必須パラメータが欠落しているなら処理を実行するべきではないのだが、謎めいたサービス精神により初期値の補填が行われていた
- その後、必須パラメータが欠落している場合はエラーを返すよう改修が行われた
事例2: 例外の隠蔽
- プログラムの中で例外をcatchして握り潰している処理が存在した
- 例外処理で適切なエラー処理が行われていない為、不正な処理結果が返されていた
- 調査の結果、経験の浅いエンジニアが実行時エラーを回避する為に闇雲にtry〜catchを入れたものだった
- 適切なエラー処理を行うよう改修が実施された
事例3: 事後条件の不足
- APIが返却するデータが仕様と異なることが時折発生していた(その為、そのデータを利用するアプリが度々エラーになっていた)
- 根本原因はAPIが参照するDBのデータの整合性が損なわれていたことだった
- APIが参照するデータとそれに紐づくレコードが存在していなかった為、APIのレスポンスデータの特定の項目が丸ごと欠落していた
- API側ではレスポンスデータの形式チェックを行っていなかった為、API仕様で定義されたものとは異なる形式のレスポンスを返却してしまっていた(事後条件を満たしていないことを検知できていなかった)
- APIのレスポンスデータを構築する処理に形式チェックを追加し、データ形式に問題がある場合はエラーとなるよう改修した
- この改修によりDBのデータ不整合も早期に発見できるようになった