やってはいけない実装
はじめに 1. 影響範囲が読めないコードの例 2. 仕様とコードの因果関係を分かりやすくする 3. 開発者にとって怖いのは「影響範囲が読めないコード」 4. 良いコードとは「変更の影響範囲が読めるコード」 はじめに 「とりあえず動くものを作る」。 ソフトウェア開発では、どうしてもこの誘惑があります。 目の前に要件があり、それを満たすために必要なAPIを調べ、コードを書き、動作確認をする。テストも通った。これで完成です。 しかし、その実装が半年後、1年後も安全に変更できるとは限りません。 私は最近、ソフトウェア開発において特に避けるべきなのは、単に「バグのあるコード」や「複雑なコード」ではなく、 変更したときの影響範囲が読めないコードを書くこと なのではないか、と考えるようになりました。 そして、そのようなコードが生まれる大きな原因の一つが、 仕様や利用する技術の仕組みを十分に理解しないまま、目の前の要件を満たす実装をしてしまうこと なのではないかと思います。 1. 影響範囲が読めないコードの例 例えば、ECサイトに「購入金額が1万円以上なら送料無料」という仕様があったとします。 目の前の要件を満たすだけなら、注文処理の中に、 if (totalPrice >= 10_000) { shippingFee = 0 } のような処理を追加すれば十分です。 実際に動作しますし、テストも通るでしょう。 ところが、しばらくして仕様が変更され、 「1万円以上でも、一部の商品は送料無料の対象外とする」 となったとします。 すると、先ほど追加した処理を変更する必要があります。 さらにその後、 「会員ランクがゴールド以上なら、5,000円以上で送料無料」 という仕様が追加されたらどうでしょうか。 最初は注文処理の中に直接書いた条件が、次第に複雑になっていきます。 if (totalPrice >= 10_000) { shippingFee = 0 } else if (isGoldMember && totalPrice >= 5_000) { shippingFee = 0 } さらに商品ごとの例外が追加され、キャンペーンが追加され、地域による違いが追加され……。 最初は「数行追加するだけ」だった実装が、いつの間にか、 「送料の仕様を変更したら、どこに影響するのか分からない」 という状態になってしまいます。 ここで重要なのは、最初の実装が間違っていたとは限らないことです。 その時点では、要件を満たしていたかもしれません。 問題なのは、目の前の要件だけを見て実装した結果、後から仕様との関係を追いにくくなってしまったことです。 2. 仕様とコードの因果関係を分かりやすくする では、どうすればこのような状態を防げるのでしょうか。 一つの方法は、 仕様に存在する概念やルールを、コード上でも分かる形にすること だと思います。 先ほどの例なら、「送料」という単純な値を注文処理の中で直接変更するのではなく、 注文 ↓ 送料を計算する ↓ 送料無料条件を判定する ↓ 送料を決定する という仕様上の流れが、コードからも読み取れるようにします。 ...