背景
早期PoC段階から、まだ事業が成長してもないし、ローンチすらしていない段階から
拡張性や再利用性を考慮し、マイクロサービス化を検討
という明らかにオーバーエンジニアリングすぎる要望の案件に遭遇したのが、
この記事を書くに至った背景です。
結論
早期過ぎる拡張性
まだ事業が成長するかもわからない段階で、拡張性やらを無理に考慮しては、
かえってアーキテクチャが運用できないほど複雑になる
後々になって拡張が不要
リスクに太刀打ちできない不可逆なアーキテクチャ
早期過ぎる再利用性
早期からの再利用性は、そこへの変更時のクライアントコードへの変更リスクが高い
ある程度コンポーネントの品質が安定してきてから、リソース競合を起こさないように、徐々に再利用の重みを増やしていくべきである。
なぜその考えが正しいのか
この根底思想には、リーンやアジャイル開発の重要な原則を反映しています。
1. 「YAGNI(You Ain't Gonna Need It)」の原則
まずこれは、ソフトウェアアーキテクチャだけでなく、
ビジネスアーキテクチャでもインフラアーキテクチャでも言えることです。
「それはまだ必要ない」という原則の通り、事業が成功するかどうかも不確かなPoCの段階で、使われるかどうかわからない将来の拡張性を考慮するのは、典型的なオーバーエンジニアリングです。
事例
たとえば、不要な拡張性のためのinterface導入は、理解しにくく変更しにくい、複雑なアーキテクチャを生み出すだけです。
デザインパターン盛り込み大好きさんがこれによく陥っています。
PoC段階でのアーキテクチャの最も重要な役割は、仮説検証のサイクルをいかに速く回せるかであり、将来の拡張性ではありません。
2. 「再利用性」は「発見」されるものである
優れた抽象化は、発明されるのではなく、発見される
早期の再利用のリスク
まだ仕様が不安定で品質も担保できていないコンポーネントを共通化(再利用)してしまうと、そのコンポーネントに1つ変更が入っただけで、依存する全ての外部コンポーネントが影響を受ける 「密結合」な構造を生み出してしまいます。
共通した再利用したいなと思える部分は、最初、品質は不安定なものです。
よって、変更が入るリスクは当然あると言える段階です。
その段階での、図のような共通化は、変更時のリスクを跳ね上げるだけでなく、
変更容易性から始める
最初は、意図的にDRYに反してでも、ある程度の重複を許容して、各コンポーネントを疎結合に保ち、それぞれを独立して迅速に変更できる状態を維持します。
再利用性の導入
開発を進める中で、複数の場所で「同じようなコードが繰り返し現れる」というパターンが発見されたら、それは初めて、その部分を共通化・再利用する価値があるという強力なシグナルです。
この段階でリファクタリングを行うことで、本当に必要な、正しい共通化を導入できます。
考慮点
ただし、上記のオーバーエンジニアリングすぎるといったものは、
何も考えるなということではないです
将来を見据えた「良いアーキテクチャ」を構築するためには、以下の点も考慮に入れると、より戦略的なアプローチになります。
「変更容易性」の先にある「拡張性」
早期段階では「変更容易性」を優先すると述べましたが、この変更容易性を維持したまま、
将来的に「拡張性」や「再利用性」を高めるためには、最初から全く何も考えないわけではありません。
例えば、関心の分離の原則は、拡張性や再利用性のためにも重要ですが、同時に変更容易性の基盤でもあります。
初期からモジュールの役割を明確にしておくことは、将来のコンポーネント分割や再利用を容易にする土台になります。これはオーバーエンジニアリングとは別物です。
「再利用」の定義
再利用性には複数のレイヤーがあります。
コードの再利用
質の低いコンポーネントの安易な再利用は避けるべきです。
「アーキテクチャパターン」の再利用
早期から、モジュラーモノリスやレイヤードアーキテクチャのような、将来のマイクロサービス化も視野に入れた拡張性の高いパターンを採用しておくことは有効です。
やりすぎは禁物ですが
これは、特定のコンポーネントを再利用するわけではなく、設計思想を再利用するというアプローチです。
「アジリティ」と「オーバーエンジニアリング」の境界線
アジリティ(俊敏性)は、単に速くコードを書くことではなく、ビジネスの変化に素早く、かつ持続的に対応する能力です。
完全に「変更容易性」以外を考慮ゼロで始めると、事業が成功した際に、その後の機能追加やスケールが全くできなくなる 「アーキテクチャの負債」 を抱えることになります。
この負債の返済は、ゼロから設計し直す以上のコストがかかることもあります。
このため、「将来を見越した設計」と「過剰な設計」のバランスが重要になります。
段階的
初期のPoCやMVPフェーズにおいて、リスクを最小限に抑え、成功確率を最大化するための、現実的な戦略として以下をお勧めします。
この戦略は、
「まずは小さく始めて、検証し、成功したら賢く成長させる」
という、スタートアップや新規事業開発における最も成功しやすいアプローチと言えます。
①. PoC/MVPフェーズ
「変更容易性」を最優先し、ビジネスが成功するかどうかを検証することに集中します。
この段階では、将来の予測に基づいた過剰な投資を避けます。
②. 成功後のフェーズ
事業が軌道に乗ったと判断されたら、「段階的なリファクタリング」 に投資します。
この段階で、PoCで得られた実際の知見(どの部分がボトルネックになるか、どの機能が成長するかなど)に基づいて、「本当に必要な」拡張性や再利用性をアーキテクチャに組み込んでいきます。
「トリガーベースのアーキテクチャ進化」という考え方
では、段階的なリファクタリングのために必要な判断基準は何でしょうか?
「YAGNI」と「将来を見越した設計」という2つの対立しがちな概念を両立させるための、極めて実践的な方法はないのでしょうか?
こういう時のために、適応度関数やアラートというものが必要になってきます。
どういうことか?
まずは手早くアーキテクチャ組んでみます。
拡張性の観点
その上で、おおよそどのくらいのSLOを再現性高く叩き出せるのか?を明らかにした上で、
おおよその閾値を定義ます。
その値の70%程度(この数字も仮置き)に毎日のように差し掛かるようになってきたなら、
流石に拡張性を考慮するようにして、どうアーキテクチャを変えるのか?を計画立てる。
再利用性の観点
再利用性であれば、変更頻度が落ち着いて来て、一定以上品質も担保されたモジュールがあり、そのコード内容を他の文脈からも使いまわしたいという時に、ゆくゆくは共有ライブラリとして定義したくなる時が来ます。
事前におおよその「このくらいの変更頻度に落ち着いて来て、かつ重複箇所が何か所以上になってきたら再利用性を重視し、共通コード化する」といった具合に。
「このラインに来たら計画を立て、このラインに来たらその計画を実行する」
という閾値を定義しておくことが重要です。
「観測可能な指標」をトリガーとして、アーキテクチャの進化をプロアクティブ(能動的)に、かつ計画的に進めるという、非常に高度ではあるものの重要なアプローチです。
1. 観測可能な指標の定義
まず、アーキテクチャの状態を客観的に示す指標を定めます。
拡張性に関して:
SLOの達成率、CPU/メモリ使用率のトレンド
再利用性に関して:
モジュールの変更頻度(コミット履歴)、コード品質(カバレッジ、バグ密度)
2. 進化の「閾値(トリガー)」の事前合意
次に、「もしこうなったら、こう動く」 という閾値と運用アクション内容を事前にチームで合意しておきます。
これも含めての運用定義だと私は思います。
具体的には、以下のように定義しておきます。
CI/CDにこの適応度関数とアラートが組み込めると、なおのこと良いです!!
拡張性のトリガー
もし、SLOの閾値の80%に達する日が週に3回以上観測されたら、その時、事前に準備しておいた「スケールアウト戦略の検討」という計画を開始する。
再利用性のトリガー
もし、あるモジュールの変更頻度が過去6ヶ月で5回未満になり、品質基準(例: コードカバレッジ90%以上)を満たし、かつ、別のチームからそのロジックの再利用要求が正式に出されたら、その時、共有ライブラリ化の計画を実行に移す。
3. 計画と実行の分離
「計画を立てる」ことと、「計画を実行する」ことを分離することで、
アラート疲れをしないように事前にしておきましょう。
計画を立てるべき閾値に来たら黄色信号アラートで、この段階からすでに計画は考えておく。
実行すべき閾値に来たら赤色信号アラートで、上の計画段階で考えておいたプランを実行に移す。
これにより、問題が顕在化してから慌てて解決策を考えるのではなく、問題が顕在化する兆候が見えた時点で、冷静に計画を立て始めることができます。
モノリシックで挙動の予測が比較的立てやすい場合には、
カオス実験のような鬼めんどいことをやるのではなく、このような仕組みで対処しましょう。
もたらす効果
この「トリガーベースの進化」モデルは、以下のような絶大な効果をもたらします。
意思決定の高速化
事前に合意があるため、問題発生時の議論や混乱を最小限に抑え、すぐに行動に移せます。
プロアクティブな運用
常に受動的に問題に対応するのではなく、兆候を捉えて能動的にアーキテクチャを改善できます。
これによって、例えば
「不確実な挙動Xの変域が、このくらいの値になってきたら、挙動の予測容易さが大幅に下がってきているので、カオス実験を計画するようにする」
といったように、定量的にカオス実験の計画・実行を判断できるようにしておくことも大事と感じます。
客観的な基準 -ガバナンスアーキテクチャの運用-
「なんとなく不安だから」といった主観ではなく、データに基づいてアーキテクチャの変更を判断できます。
もちろん、慣れてきた人がいちいちデータに頼ることなく、直感的に
「そろそろやばいぞ」っていうのは、それはそれで価値があるものです。
これらの思考法は、アーキテクチャを「一度作ったら終わり」の静的な設計図ではなく、
ビジネスと共に成長し続ける一種の「生命体」として捉え、その進化のルールを設計するというガバナンスアーキテクチャにも繋がっていくわけです。
まとめ
アーキテクチャの初期段階(PoC/MVP)では、
将来の不確実性に対応するため、拡張性や再利用性よりも「変更容易性」を最優先する。
プロダクトが安定し、共通化の必要性がデータによって証明されてから、初めて
「再利用性」や「拡張性」を高めるリファクタリングを行う。
これは、ビジネスの不確実性を真正面から受け止め、リスクを最小化しながらプロダクトを育てるために必須の思考と思ってほしいです。
そして、適応度関数などの仕組みを使って、
定量的にどのくらいになったら、再利用性や拡張性といった他の品質項目も
アーキテクチャ特性に盛り込んでいくのか?というPDCA基盤体制も、運用の要件として事前に定義しておきましょう。
超応用
これはかなり応用なんで、読み飛ばしていただいてもかまいません。
ただ、超上級者になりたいのなら、是非読んでください。
先程、トリガーベース と書きましたよね?
実はこれ、イベントとして考えられます。
これらの記録をイベントソーシング的に残すようにすることで、
安全にアーキテクチャの効果測定や、移行を進めやすくなります。
もちろん、そのADR運用基盤を構築するコストや複雑さはありますが