犠牲的アーキテクチャ
2014年10月20日
あなたは会議に出席し、チームが過去数年間取り組んできたコードについて熟考しています。そして今できる最善のことは、そのコードをすべて破棄し、まったく新しいアーキテクチャで再構築することだと判断しました。その運命にあるコード、それに費やした時間、そして当時行った決定について、どのように感じますか?
多くの人にとって、コードベースを破棄することは失敗の兆候です。ソフトウェア開発に内在する探索的な性質を考えると理解できるかもしれませんが、それでも失敗です。
しかし、多くの場合、今書くことができる最高のコードは、数年後に破棄するコードです。

私たちはしばしば、優れたコードを長寿命のソフトウェアと考えています。私はこの記事を、1980年代にまで遡るエディターで書いています。ソフトウェアアーキテクチャに関する多くの考えは、そのような寿命を促進する方法です。しかし、成功は、ずっと前に `/dev/null` に送られたコードの上に築かれることもあります。
Webで最も成功した大企業の1つであるeBayの話を考えてみましょう。それは、1995年に週末にかけて構築されたPerlスクリプトのセットとして始まりました。1997年には、すべてが解体され、当時のWindowsツールの上にC++で書かれたシステムに置き換えられました。そして2002年には、アプリケーションは再びJavaで書き直されました。これらの初期バージョンは、置き換えられたため、エラーだったのでしょうか?とんでもない。eBayはこれまでのWebの大きな成功例の1つですが、その成功の多くは90年代の破棄されたソフトウェアの上に築かれました。多くの成功したWebサイトと同様に、eBayは指数関数的な成長を遂げてきました。そして、指数関数的な成長はアーキテクチャの決定には優しくありません。1996年のeBayをサポートするのに適したアーキテクチャは、2006年のeBayに適したアーキテクチャにはなりません。1996年のバージョンは2006年の負荷を処理できませんが、2006年のバージョンは、1996年のニーズに合わせて構築、保守、および進化させるには複雑すぎます。
実際、このガイドラインは組織の働き方に組み込むことができます。Googleでは、明示的なルールは、システムを現在のニーズの10倍の規模で設計することです。これは、ニーズが桁違いに増加した場合、最初から破棄して置き換える方が良いことが多いことを意味します[1]。サブシステムが数年間隔で再設計および破棄されることはよくあります。
実際、成熟したコードベースに参入してきた人々が、そのパフォーマンスやスケーラビリティの欠如を非難するのはよくあるパターンです。しかし、ソフトウェアシステムの初期段階では、実際に何をする必要があるかが明確でないことが多いため、パフォーマンスや可用性よりも機能変更の柔軟性を重視することが重要です。後でユーザーが増えるにつれて優先順位を切り替える必要がありますが、パフォーマンスの低いコードベースに過剰なユーザーを抱えることは、通常、その逆よりも良い問題です。ジェフ・アトウッドは「パフォーマンスは機能である」というフレーズを作り出しました。これは、パフォーマンスは常に最優先事項であるという意味で読まれる人もいます。しかし、どの機能も他の機能と比較して選択する必要があるものです。それは、パフォーマンスのようなものを無視すべきだと言っているわけではありません。ソフトウェアはビジネスを破綻させるほど十分に遅く、信頼性が低くなる可能性があります。しかし、チームは他のニーズとの難しいトレードオフを行う必要があります。多くの場合、これらはテクノロジーよりもビジネス上の決定です。
では、意図的に犠牲的アーキテクチャを選択するということはどういう意味でしょうか?本質的に、それは数年後には(うまくいけば)現在構築しているものを破棄する必要があることを今受け入れることを意味します。これは、構築しているものの機能横断的なニーズに制限を受け入れることを意味する場合があります。それは、時期が来たときに交換を容易にすることができるものについて今考えることを意味する場合があります。ソフトウェア設計者は、その作成を設計して、その優雅な交換をサポートする方法について考えることはめったにありません。それはまた、比較的短い時間で破棄されるソフトウェアでも、多くの価値を提供できることを認識することを意味します。
アーキテクチャが犠牲的であることを知っているということは、ソフトウェアの内部品質を放棄することを意味するわけではありません。通常、内部品質を犠牲にすると、コードベースの廃止に取り組んでいない限り、交換時間よりも早く問題が発生します。優れたモジュール性は、健全なコードベースの重要な部分であり、モジュール性は通常、システムを交換する際に大きな助けとなります。実際、システムの初期バージョンで行うのに最適なものの1つは、交換のためにその知識に基づいて構築できるように、最適なモジュール構造を検討することです。初期段階ではシステム全体を犠牲にすることが合理的である場合がありますが、システムが成長するにつれて、個々のモジュールを犠牲にする方が効果的です。これは、適切なモジュール境界がある場合にのみ実行できます。
この問題の処理に関して見落とされがちなことの1つは、会計です。そうです、本当に—コードベースの償却方法のために、明らかに実行不可能なシステムの交換に消極的な人々がいる状況に遭遇しました。これは大企業にとって問題になる可能性が高いため、その世界に住んでいる場合は確認することを忘れないでください。
この原則は、既存システム内の機能にも適用できます。新しい機能を構築している場合、ユーザーのサブセットのみに利用できるようにするのが賢明なことがよくあります。そうすることで、それが良いアイデアかどうかについてのフィードバックを得ることができます。そのためには、最初は犠牲的な方法で構築する必要があるかもしれません。そうすれば、完全な展開に値しないことがわかった機能に、すべての労力を投資する必要はありません。
モジュール交換可能性は、マイクロサービスアーキテクチャを支持する主要な議論ですが、犠牲的アーキテクチャにそれを推奨することには慎重です。マイクロサービスは、分散と非同期性を意味しますが、どちらも複雑さを増大させます。私はすでに、実際に必要とせずにマイクロサービスのパスを採用したいくつかのプロジェクトに遭遇しました。結果として、機能パイプラインが大幅に遅くなりました。そのため、モノリスはしばしば優れた犠牲的アーキテクチャであり、マイクロサービスは後で徐々にそれを分解するために導入されます。
犠牲的アーキテクチャを作成するチームは、それを犠牲にする時期を決定するチームです。これは、新しいチームが参入し、既存のコードを嫌い、書き直したい場合とは異なるケースです。自分が書いていないコードを、それが書かれたコンテキストを理解せずに嫌うのは簡単です。自分のコードを意識的に犠牲にすることは、まったく異なるダイナミクスであり、これから書くコードを犠牲にすることを知っていることは、その有用なバリエーションです。
謝辞
ランディ・シュプとの会話は、この投稿を奨励し、策定するのに役立ちました。特に、eBayの歴史(およびGoogleからの同様のストーリー)について説明しました。ジョニー・リロイは会計の問題を指摘しました。キーフ・モリス、ジェイソン・イップ、マヘンドラ・カリア、ジェシカ・カー、ラフル・ジェイン、アンドリュー・キエラー、ファビオ・ペレイラ、プラモード・サダレージ、ジェン・スミス、チャールズ・ヘインズ、スコット・ロビンソン、ポール・ハーマントが有益なコメントを提供してくれました。
注記
1: ジェフ・ディーンが述べているように、「約10倍の成長を見込んで設計するが、約100倍になる前に書き直す計画を立てる」