例外処理への考察からTupleでエラーを返すまでの経緯

今まで外部委託のソースコードをレビューしてきたけど、大体が例外の処理を正しく出来ていない。キャッチしながらもスルーするのはほんのご愛想、中には正常処理の一部として利用しているなんていう酷いものもある(他システムへの命令で例外が発生したのに、あたかも正常処理として扱われていた。何を言っているのか(ry)。ここは「そもそも例外処理は何なのか」を振り返る必要があるということで書き出してみました。ちなみに例外処理をディスる気持ちはありません。一応メリットも挙げていますしね。

例外処理って何なの

例外処理 - Wikipediaより以下引用します。

例外処理(れいがいしょり)とは、プログラムの上位の処理から呼び出されている下位の処理で継続不能、または継続すれば支障をきたす異常事態に陥ったとき、制御を呼び出し元の上位の処理に返し安全な状態になるよう回復処理をすること。その際に発生した異常のことを例外と呼ぶ。

全くその通りなんですが、要は「処理が続けられないですよ」ということ。それを無視したり、あまつさえ正常処理として利用するなんざご法度です。

例外処理のメリット

恒常的にあるはずのファイルがなぜか消えていた、ファイルの権限が書き換わりアクセスできない、相手先サーバが落ちて接続できない、データベースに接続できない、といった理由で一連のトランザクション処理を飛ばしたいといったときには例外処理が適してます。この場合、業務ロジック中(ここでは業務手続き処理の意)ではキャッチせず、フレームワーク側でキャッチするのが正しいでしょう。ただし、データベース処理のロールバックについては業務ロジック中に記載が必要ですけどね。

例外処理のデメリット

そもそも例外処理は扱い難いです。その理由として、プログラマ側としてはどこで例外をキャッチすべきかという設計センスが必要になること、レビュアー側としては処理を一気に飛ばされるため手順通りに追えないことがあります。特に例外のキャッチ場所については要注意、下手するとGoto文の再来です。そもそも手順をかっ飛ばして別のセンテンスに移るのは本来害悪でしかありませんから。

例外処理に代わるエラーの代替処理

エラーを戻り値として戻す方法があります。Golang関数型言語のEitherなんかが良い例です。例えば、業務上起こりえるエラーで尚且つ処理を続けるための代替手段がある場合については、やはり戻り値として返し、プログラマ側で意図的に処理してもらうのが一番でしょう。直近では、入力値が不正、ユニークなデータレコードが既に存在している、ユーザのデータアクセス権限がないという異常処理についてはこの方法が適していると思います。

私はGolangを見習い、業務上起こりえて尚且つプログラマにきちんと処理してもらいたいエラーについてはTupleを利用して返すようにしています。それをプログラマがどのように処理するかは勝手です。例外を発生させるでも良し、無視を決めるでも良しです。もちろん、レビューした上で意図を詰めるつもりですけどね。

総括

エラー処理も需要に合わせ多様になっても良いかと思います。Golang関数型言語のEitherの中で返り値として渡すのは良い傾向ですよ。