オーバーエンジニアリングの正体とその向き合い方

問題は細部(あるいはその欠如)にあり。

多数の刃物を1つに納めたクレイジーなスイス製アーミーナイフ

議論とは、ソフトウェア開発の基本的な構成要素であり、スケーラビリティを向上させるためには避けられない摩擦であると言えます。議論を通して私たちは出来上がるものの品質に影響を与え得るような問題を早い段階で浮かび上がらせることができるのです。その1つがオーバーエンジニアリングの問題です。

ウィキペディアによると、オーバーエンジニアリングとは下記のとおりです。

十分な安全率や十分な機能の確保のためか、あるいはデザイン上の誤りのどちらかの理由から、アプリケーションが必要とする以上に強固で複雑なプロダクトがデザインされてしまうこと。

また、ウィキペディアには、オーバーエンジニアリングが好ましい場合として、さらに、このようなことも書いてあります。

ある特定の基準の下で安全やパフォーマンスが非常に重要である場合、あるいは、極めて広範囲の機能が要求される場合がある。しかし一般的にはバリューエンジニアリングの観点から無駄が多いと批判される。

開発者のコミュニティに目を向けると、更に別の考え方も見つかります。StackOverflowのユーザJeff Sternalによると、オーバーエンジニアリングに共通する兆候とは、以下のことです。

自分が抱えていない問題を解決するコード

別のユーザであるOscarは、ソフトウェア開発のコンテキストにおいてオーバーエンジニアリングがどういうものかを図示しています。


注釈: (タイトル部分、左から)
バランスのとれたエンジニア/自己陶酔型のエンジニア/ナルシストでエゴの強い反社会的なエンジニア

セカンドシステム症候群のページに、”小さくて、エレガントで、よくできたシステムは、大きな期待を持たれてしまうため、フィーチャー・クリープの被害を受けやすい”と書かれています。ソフトウェアの開発では、プロジェクト・ステークホルダ(エンジニアも含む)が、プロジェクトに本質的な価値が加わることを期待して必要以上の構築をしようとした時にも、セカンドシステム症候群が見られます。これらは、実際の要因ではなく、将来への思惑によって、期待が膨らむという点で異なっています。ユーザの要件からエンジニアリングへ進む過程で推察がかけ離れてしまい、問題が最大化するのです。また、その逆の過程でも同じことが起こります。

木にブランコをかける計画を例にし、従来の開発ライフサイクルにおける各パートでの間違いを描いた有名なイラスト
注釈:
(左上→右上)
顧客が説明した内容
プロジェクト・リーダーが理解した内容
エンジニアが設計した内容
プログラマがプログラミングした内容
営業が商品の特徴を説明した内容
(左下→右下)
プロジェクトを文書化した内容
組み込まれた働き
顧客に宣伝された内容
ヘルプデスクがサポートする内容
顧客が本当に必要とする内容

オーバーエンジニアリングとは、思惑によって本当に必要とされるもの以上を構築しようとすることである。

それでも私たちは、”思惑によって本当に必要とされるもの”とは実際に何なのか、ということを知らなければいけません。これは、客観的な基準ではなく主観的な議論によって答えが変わる、適切なソリューションレジビリティの議論と同じジレンマに要約されるでしょう。

簡単な例として、クリック時に要素のclass属性を変更する例を考えてみましょう。以下が要件です。

クリック可能な3つのイメージを構築する。それぞれのイメージをクリックすると、クリックされたイメージが倍のサイズまで大きくなっていき、1秒後に元のサイズまで小さくなっていく。

上の例では、実装に大きなパターンやフレームワークは使いません。要素をクリックすると、それが大きくなって、1秒後に元のサイズに戻ります。

明らかに重複している部分がいくつかあるので、1つの関数にまとめてみましょう。

これは十分なソリューションでしょうか。恐らくそうだと思います。

少し戻ってみると、クリックのライフサイクルには、クラスの組み込み、1秒の遅延、クラスの削除、という3つの共通ステップがあることに気付くでしょう。ステップのどれかに何らかの操作を加える必要がある場合は、各ステップの属性ではなく、関数を変えなければならないでしょう。jQueryには隠れた機能があり、操作の遅延キューを作ることができます。これは各ステップ内の機能を分けることに利用できます。(いやまあ、ドキュメント化されていない機能は存在しないことは知ってますし、それはjQueryのprivateメソッドでもないことも知ってますよ)

上の例では操作を3つのステップで構築しています。各ステップの関数は、関数の内容を変えることで、別々に展開できます。各ステップの間ならどこにでも、遅延を実装できます。

ここでの問題は、各ステップで多くのnext()を呼び出していることです。私たちが気にしているのは、ステップが変わり、非同期フローを使用しないのはいつか、ということだけなので、next()を関数に取り込んでしまえば明示的に呼び出す必要はなくなります。

現段階では、最初の例よりはコードの数が少なくなりました。ステップはワンライナーで表現されていますし、変更される可能性のあるものだけが繰り返し書かれています。

反論される可能性があるとすれば、jQueryだけではこの問題に対するソリューションは十分ではないので、AngularやReact、VueJSなどでアプリケーションを開発する必要があるという点です。なぜなら、アプリケーションがさらに良くなる可能性が高いですし、後から変更するコストも高くなることが予想されるからです。

ちょっと待ってください。なぜ私たちは、フレームワークやキュー、ステップのことを話しているのでしょう?

元々の要件に戻りましょう。

クリック可能な3つのイメージを構築する。それぞれのイメージをクリックすると、クリックされたイメージが倍のサイズまで大きくなっていき、1秒後に元のサイズまで小さくなっていく。

これはオーバーエンジニアリングでしょうか? もしそうなのであれば、どの点においてでしょう?


注釈:
このマンガの内容はこうです
「塩を取ってくれる?」
「ちょっと…」
「分かってる! 今、勝手に調味料を渡してくれるシステムを開発中なんだよ」
「もう20分も経つじゃないか!」
「結果的に時間の節約になるんだ!」

問題は、提案されたソリューションについてではなく、要件についてだと思われます。ソリューションを実装しコード化する代わりに、エンジニアは一歩引いて、「なぜ?」と自問することによって、問題の根源に対して気を配るべきなのです。

オーバーエンジニアリングではないかと考えてしまう機会を減らすには、問題を理解する必要がある。そして問題を理解するには、可能な限り「なぜ?」を自問し、要件を明確化する必要がある。

もちろん原則として、すでに発見されているソフトウェアの原理の利用は、オーバーエンジニアリングと考えるべきではありません。しかし、要件が2つのボタンをいったん大きくしてまた元に戻すということだけだったとしても、ある人は拡張性を持たせて分離(decoupled)しておくことは有益であると主張するでしょうし、またある人は要件が大規模なシステムの一部な場合に限ればフレームワークやjQueryを使った開発が正当化されるけれどそうでない限りは最初の例だけで十分だ、などと主張するでしょう。

問題は、オーバーエンジニアリングは主観的なものであり、エンジニアが解決すべき問題の全体像を要件から導くことに失敗すればするほど、その主観によるダメージが大きくなってしまうということです。しかしながら、問題の全体像が明らかであった場合であっても、より良い基盤を構築しておくことは長い目で見るとより多くの問題が起こり得るナイーブな方法を取るよりも有益であると主張する立場もあります。

オーバーエンジニアリングは主観的なものであり、エンジニアが解決すべき問題の全体像を要件から導くことに失敗すればするほど、その主観によるダメージが大きくなってしまう

長期的な目線で目的や思惑が理解できないのであれば、”オーバーエンジニアリング”しそうな要素を無視しないでください。そして一歩引いて考えて、問題の全体像を理解する前に必要以上のことをしようとしないことです。

結局のところ、オーバーエンジニアリングの定義は、個々の状況や背景によってまちまちです。プロジェクトにかかわった人自身がそれぞれの意見に基づいて、何がオーバーエンジニアリングで、何がそうではないのかを決めることになるでしょう。

残念ながら、常に何がオーバーエンジニアリングであるのか、そして何がそうではないのかという質問に共通の答えはありません。

私たちはそれを受け入れなければいけないのです。

何かご意見があれば、TwitterFacebookGitHubに投稿をお願いします。


記事を楽しんでいただけましたか? より多くの記事をサポートするには、この記事を他の人にもシェアしてください。「お勧め」をクリックしていただいても結構です。

2016年10月27日編集:
MoreMoschopsからいただいた意見を基に、例として使用した要件を改善しました。