🙄

DRY原則を間違って理解しないでPlease

2022/01/19に公開

エンジニアの中二病とも言うべく誰しもがかかってしまう病があります。それがあやまったDRYの理解と共通化したい病です。

それぞれどういった病気なのか、どういった症状が現れるのか解説していきます。

共通化したい病

プログラミングし始めた頃は、とりあえずプログラムが上手く動くように処理を上から書いていくと思います。ただ一年くらいしてプログラムを書くのになれてくると、メンテナンス性の高いコードを書こうといろんな原理原則を学び始めるのです。それ自体は間違ってないのですが、私がみてきた2,3年目のエンジニアは共有化をすることが正義だと考えすぎてそのせいで読みにくいコードと技術的負債を作ることにコミットしてしまっている人がほとんどでした。

誤ったDRYの原則の解釈について

DRY(Don't Repeat Yourself)の原則はエンジニアであれば少なくとも一度は耳にしたことがあるのではないでしょうか?

Andy HuntとDave Thomasが「達人プログラマー―システム開発の職人から名匠への道」で提唱したソフトウェア開発原則です。
信頼性の高いソフトウェアを開発して、開発そのものを簡単に理解したりメンテナンスできるようにする唯一の方法は、DRY原則に従うことです。
すべての知識はシステム内において、単一、かつ明確な、そして信頼できる表現になっていなければならない。
何故これがDRY原則なのでしょうか?
DRY原則を破るということは、同じ知識を2箇所以上に記述することです。この場合、片方を変更するのであれば、もう片方も変更しなければならないのです。さもなければ異星人のコンピュータのようにプログラムは矛盾につまづくことになるのです。これはあなたが覚えていられるかどうかという問題なのではありません。これはあなたが忘れてしまった時の問題なのです。
このDRY原則は本書中のコーディングに関係のない部分でも何度も登場します。我々はこれが達人プログラマーの道具箱の中にある道具のうちで最も重要なものの一つであると考えています。
達人プログラマー―システム開発の職人から名匠への道

これだけを読んでしまうと、たしかに重複したコードを書くことが悪であるかのように思えてきます。ですが実際のところは、ここに書かれていない前提条件として同じドメイン知識のものであれば必ず共通化させるべきだということを言っています。

なぜDRYしたい疫病が進んだかというとRuby On RailsがDRYの原則を採用しているというところが起因していると思います。Railsが爆発的にはやってしまい、DRYの原則を文面だけで鵜呑みにしてしまうエンジニアが量産されて、共通化してはいけない異なる知識を共通化させて負債化させることが進んでしまっています。

同じ処理だけど共通化させてはいけないところ

アプリケーションが次のようにそれぞれの役割に分かれていたとします。

このときに、データ取得するクラスとそれを利用するクラスで、たまたま同じような処理が出てきたとします。例えば、validate()のような値を確認するような処理だったとしましょう。処理の中身も同じだったのでどちらか一方に書いて他方ではそれを参照させようという意識が芽生えてくるかもしれません。ただし、それがほんとうにどちらのクラスでもやる必要があるとしたら共通化はさせるべきではありません。

技術的負債になるのは次の場合です。

別の実装者が機能改修をしようとして、誤ったDRYによって共通化されてしまった箇所を「ここを修正したらいいのか」とか、あとはドキュメントやテストがない場合は、設計の意図をくみとりながら「ここを修正したらいいのか」といった感じで修正していくと思います。

そうしたときに、下記のような負の流れを作ってしまいがちです。

  • 共通化をしてしまったせいで誤った場所に実装が増えていく。
  • 思っても見ないところに実装があって障害につながる。
  • そこからすべてのソースコードを見ないと処理がわからない。
  • 調査コストの増加など負のスパイラルにつながる。

といったように次第に技術的負債が積み上がっていきます。

無理やり抽象化させてデザインパターンを適応しようとする

DRYの原則の誤った理解とはまた別の話なのですが、誤った抽象化とデザインパターンについてもいくつかポイントを紹介します。

とあるスタートアップのお手伝いをしていた時の話です。参加したときの初期の頃に決済処理のバグ取りをしていました。本来であれば大した修正のコストは掛からないはずなのですが、うまく行ってない共通化を施されていたので少しの修正で莫大なコストが掛かっていました。

どういった抽象化かについて紹介します。

使用されていた決済処理は、LINE PAYとクレジットカード決済のためにstripeが導入されていました。まずは適応されていたデザインパターンは、ストラテジーパターンでした。

ストラテジーパターンは決済という処理を抽象化していて、切り替えを行えるようにIntarfaceが用意されていました。ただ問題は、決済という意味では同じですがそれぞれ、独自のサブスクリプション(定期購読)の仕組みやキャンセルの仕組みとLoginコールバックの仕組みなどもあったので、実態は完全な別の決済処理でした。

これをむりに抽象化してストラテジーパターンを適応していたので、必要な情報を取得するのも一苦労で一部修正したら多くの場所に影響を与えていました。

共通化して良いものというのは、何かしらの規格があればそれをもとに抽象化するのは高い確率で成功しますが深い理解がないのであればそれぞれで処理系をわけて、絶対に大丈夫だという自身が出たところで共通化するのが正しい開発フローでしょう。

まとめ

あやまったDRYの原則などがあって、特に開発経験がすこし出てきたところですべての原則を開発に適応しようとするヒトもいますが、それは誤りです。デザインパターンも同じです。用法と用量を守って開発をしましょう。

Discussion

ログインするとコメントできます