見出し画像

未来の修正を最小限にするプログラムとは?

コードレビューをするとき、「プログラムにバグはないか?」をもちろん見るのですが、それ以外にも「未来のバグを防げるか?」という観点も見るようにしています。
プログラムは日々修正されていくものなので、現在だけでなく未来も見据える必要があります。未来のバグを防ぐための方法の一つは、「未来の修正を最小限にすること」です。修正がなければバグが入りません。今回はプログラミングの初級者向けに「未来の修正を最小限にすること」について、実例をあげながら解説します。この内容が「良いプログラムとは何か」を考えるきっかけになれたら幸いです。

バグはないが未来が心配なプログラム

下記サンプルはフォームで入力したユーザ情報に未入力があるか確認するプログラムです。取得したユーザ情報にはユーザ名と生年月日が含まれますが、未入力があればエラーとして例外を投げます。

/** @type {{name:string, birthday:string}} ユーザ情報 */
const user = getUser();
// 未入力が1つでもあったらエラー
if(user.name === undefined || user.birthday === undefined) {
  throw new Error("入力が不足しています。");
}

このプログラムにバグはありません。
ですが、未来を考えるとどうでしょうか?
たとえば将来的にユーザ情報に住所(address)が増えたらどうでしょう?
未入力をチェックするif文に`user.address === undefined`を加える必要があります。
もしもその修正が漏れたらバグになります。

未来も安心なプログラムにする

修正漏れを防ぐにはどうするか?
ヒントはコメントにあります。
「未入力が1つでもあったらエラー」
この通りに実装すればよいのです。
次のプログラムでは、確認対象の項目を1つ1つ記述するのではなく、コメントの通りにuserが持つ値をすべてチェックして未入力を確認します。

/** @type {{name:string, birthday:string}} ユーザ情報 */
const user = getUser();
// 未入力が1つでもあったらエラー
const hasUndefinedValue = (obj) => Object.keys(obj).filter(key => user[key] === undefined).length > 0;
if(hasUndefinedValue(user)) {
  throw new Error("入力が不足しています。");
}

このプログラムでは`hasUndefinedValue(obj)`※1 という関数を追加しました。
if文に直接書いてもよいのですが、読みにくくなるため関数化しました。
`hasUndefinedValue(obj)`の中でuserが持つすべての項目をチェックしているので
将来的に、住所や氏名カナが追加されても、未入力の確認部分は修正が不要です。
つまり修正漏れが発生しません。
よって、未来の変更にも対応した安心なプログラムになりました。

とはいえ考え過ぎは良くない

未来を見据えた実装は良いことですが、やり過ぎはよくありません。
たとえば今回はユーザ情報がすべて必須項目でしたが、未来に任意項目が必要になったらどうでしょうか?それに対応するために、今のうちに任意項目にも対応しておいたほうがよいのでしょうか?この対応は正直微妙かもしれません。「未来を見据えることは良いことだ」という考え方とは別に、YAGNI原則というものもあります。

"You ain't gonna need it"[1]、縮めて YAGNI とは、機能は実際に必要となるまでは追加しないのがよいとする、エクストリーム・プログラミングにおける原則である。

https://ja.wikipedia.org/wiki/YAGNI

まったく逆の考え方です。なのでどこまで未来を見据えるかは状況次第です。
チーム開発をしているなら、みんなで話し合い、ちょうどよい落とし所を見つけておくのがよいと思います。

まとめ

今回は「未来の修正を最小限にすること」について解説しました。
プログラムは日々修正し続けるものです。
だからこそ未来も見据えた設計が重要です。
いろいろ考えることが多くて難しいと感じるかもしれませんが、まずは「あーでもない、こーでもない」と考えて遊んでみるのが良いと思います。

※1: hasUndefinedValue(obj) の実装方法はこれ以外にもいろいろあります。Object.values()が使える環境なら`Object.values(obj).some(v => v === undefined)`が良いと思います。

この記事が気に入ったらサポートをしてみませんか?