generated at
進化的アーキテクチャ ―― 絶え間ない変化を支える

訳者 : 島田浩二


概要
著者は ThoughtWorks の人々
思想的な内容で、「どう実現するか」 や 「何を実現するか」 の詳細よりも、抽象的な内容が主
進化的アーキテクチャとは、漸進的かつ誘導的に変更していけるアーキテクチャのこと
進化的アーキテクチャの基本的な側面は次の 3 つ
漸進的な変更
継続的デリバリーのプラクティスなどが有効
適切な結合
どう実現するか
1. 進化の影響を受ける次元を特定する
2. それぞれの次元に対して適応度関数を定義

内容メモ
Martin Fowler進化的設計 (Evolutionary Design) の延長線上にあるコンセプト
進化的アーキテクチャを遂行することの核心
小さな変更を加える
システムがどう発展していくかから、誰もが学べるフィードバックループを導入する
継続的デリバリー進化的アーキテクチャを実用的にするために必要不可欠な要素

1 章 ソフトウェアアーキテクチャ
ソフトウェアアーキテクチャづくりには、全ての関心事の釣り合う解決策を探るためにビジネスやドメインの要求をその他の重要な要素とともに分析するアーキテクトの技能が必須
信頼性可動性安全性といった特性のバランスをとる
本書では、進化可能性という特性を追加する
高度に動的なシステムに変化を取り込むことは、予期せぬ結果を生じさせうる
技術やドメインにおける環境変化がいつ起こるかわからないが、変化は避けられない → 変化する前提でシステムを設計する必要
順応する必要
ソフトウェアアーキテクチャは 「後からの変更が難しい部分」 と定義されたりもするが、変更可能性を組み込むこともできるはず
環境が変わらないとしても、ビットロット (経年劣化) から保護する必要はある
進化可能性は他の全てのアーキテクチャ特性を保護するメタ特性
本書では、第一級のアーキテクチャ要素として、時間と変化を加える
漸進的な変更は、ソフトウェアアーキテクチャの 2 つの側面を表現
チームがどう漸進的にソフトウェアを構築するか
それをどう漸進的にデプロイするか
継続的デリバリーの実践が必要
アーキテクチャの変更を誘導するために適応度関数を導入
継続的デリバリーが、アーキテクチャの見方を運用すら包含するところまで拡張
技術アーキテクチャは、ソフトウェアプロジェクトの 1 つの次元にしか過ぎない
進化可能なアーキテクチャを作るには、変更を与える全ての部分を考慮する必要
アーキテクチャを概念的に切り分ける技法はいろいろあるが……
本書では次元の分類を作成するのではなく、現在のプロジェクトが有する次元を捉えることをしていく
変更したい対象が他の誰かのものなら、それを変更することは難しい
ソフトウェアアーキテクトは、チーム構造をアーキテクチャ上の目標にあわせるために仕事を移譲したり分割したりする方法についても注意を払う必要
逆コンウェイ戦略でチーム編成を行うことが理想的
チーム構造はソフトウェア開発の無数の次元に影響するものだから
進化的アーキテクチャの重要な 2 つの特徴
漸進的
誘導的

2 章 適応度関数
適応度関数は、遺伝的アルゴリズム設計で成功の定義に使われている進化的計算の概念 (それを拝借)
システム全体の適応度関数というものも考える (適応度関数が扱う個々の要素が衝突する場合のトレードオフを扱う)
進化的アーキテクチャにおける適応度関数は、必ずしもソフトウェアで実装できない場合もある (手作業の場合もある)
例えば循環的複雑度を一定以下にする、というようなものも適応度関数として使用できる
適応度関数は早期に特定すべき
大きなシステムを、より小さな適応度関数の集合を扱う小さなシステムに分割するのも助ける

3 章 漸進的な変更を支える技術
継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化』 が示した開発プラクティスを前提に、それらを活用して進化可能なソフトウェアを設計する
漸進的な変更 → アーキテクチャは小さく漸進的な変更を容易にしなければならない
2 つの側面
開発 : 開発者がソフトウェアを構築する
運用 : チームがソフトウェアをデプロイする
ソフトウェアに静的なものはない
環境の方がどんどん変わっていくので、合理的なアーキテクチャ計画には進化的変更を含める必要
時間軸を考慮する必要 ← 4 次元で考える (本書だと、3 次元目が実装詳細 (?) で、4 次元目が時間軸、という感じっぽい)
テスト可能性は無視されがちだが、アーキテクチャの一部はテストによる検証が容易
例えば Java 界の JDepend を使ったり
適応度関数は任意の所有者を持つことが可能 (共同所有を含む)
適応度関数を適切なタイミングで実行するためにデプロイメントパイプラインを活用
デプロイメントパイプラインは、個々の作業をステージに分割することを開発者に促す
別のプラクティスとして継続的デプロイメントがある (理想的だが、より高度な調整が必要)
ユーザーは小さな変更が頻繁に大量に現れることを望んではいない → 機能トグルを使用するなどする
機能トグルを使うことで、本番環境で QA タスクを実行できるようになる
NetflixChaos Monkey はホリスティックで継続的な適応度関数の例
本番コード上で、信頼できる古いやり方と新しいやり方を比較する (結果はもちろん、パフォーマンス等も)

4 章 アーキテクチャ上の結合
結合は必要悪だとみなされがちだが、結合なしに複雑なソフトウェアを構築することは困難
→ 最小のオーバーヘッドで最大の利益を得るために結合すべきアーキテクチャの次元を特定する
アーキテクチャの議論で古くから使われてきた用語
モジュール : 論理的なグループ化
全てのモジュール機構はコードの再利用を容易にする ← あらゆるレベルでコードを再利用することは賢明
コンポーネント : モジュールを物理的にパッケージする方法 (物理的な分割方法)
ライブラリ : 呼び出し側のコードと同じメモリアドレス内で実行されるコンポーネント
サービス : 独自のアドレス空間で実行されるコンポーネント
アーキテクチャ量子 : 高度な機能的凝集を持つ、独立してデプロイ可能なコンポーネント
システムが適切に機能するために必要なものをすべて含む
モノリシックアーキテクチャでは、量子はアプリケーション全体
アーキテクチャは運用可能になるまでは抽象的なもの
代表的なアーキテクチャパターン
進化させていくことは困難
nobuoka 巨大な泥団子よりはマシらしいが、あんまり違いがわからん
メッセージキューを使い、複数の異なるシステムを統合する
ESB 駆動 SOA : 進化の観点でいうと微妙
これが流行っていた当時は、オープンソースシステムの活用はあまりされていなかった → マイクロサービスのようなインスタンス数が多いものは採用されにくかった
多くのソフトウェアアーキテクチャは技術的な次元に焦点をあてているが、マイクロサービスはドメインの次元に焦点を当てる
進化の観点で優れている
アーキテクチャの量子サイズが小さいほど、進化的な変更は容易になる

5 章 進化的データ
データは進化可能なアーキテクチャを作る際に考慮すべき重要な次元
詳細は 『データベース・リファクタリング』 を読むこと
データベース構造に対しても、ソースコードを処理するのと同じようにテストして、バージョン管理して、漸進的に進むべし
スキーマを進化させるためにマイグレーションツールが役に立つ
不適切な結合
アーキテクチャの結合の議論は技術アーキテクチャの各側面に注目しがちだが、トランザクションも結合につながる
データベースによる結合には、ビジネスプロセスがどう動くかが定義されていることがあり、技術アーキテクチャによる結合よりもずっと負荷が大きい
データとデータベースの神格化は大企業に見られる機能不全
データベースをリファクタリングせずに、別の交差テーブルを追加する

6 章 進化可能なアーキテクチャの構築
進化的アーキテクチャの構築に役立つ 3 つの手順
1. 進化の影響を受ける次元を特定する
組織内でそれに関心を示すチームが関与する必要があるので、逆コンウェイ戦略が役立つ
2. それぞれの次元に対して適応度関数を定義する
既存のアーキテクチャの改良
既存アーキテクチャに進化可能性を加えられるかどうかは 3 つの要因によって決まる
コンポーネント結合 (適切な結合と凝集)
結合の技術的な側面を超えて、コンポーネントの機能的凝集についても考慮が必要
開発プラクティスの成熟度
進化的アーキテクチャの構築における唯一最大の障害は扱いにくい運用
変更を容易にデプロイできなければ、フィードバックサイクルの全ての部分が妨害される
適応度関数のつくりやすさ
エコシステムの構成要素の一部を所有していないことで、進化可能なシステムを構築する際の困難
メタ作業 (他の開発者が使うフレームワークを作るなど) は単なる作業よりも面白いため、それをしたがる開発者が多い
既存のものを使えるのにメタ作業ができるというだけで新しいアーキテクチャを構築するのはやめろ
アーキテクチャの移行
多くのアーキテクトは、モノリシックアーキテクチャからサービスベースアーキテクチャへ移行する
いきなり全体をマイクロサービスに分割するのではなく、モノリスを少数のサービスに分割するところから
トランザクション境界の分離が困難のひとつ
正しいサービスの粒度を見つけることが重要
分割方法の例
ビジネス機能グループ : コンウェイの法則にのっとる
トランザクション境界 : トランザクションの分割が最も難しいので、それを考慮
デプロイメント目標 : コンポーネントごとのリリース速度や可用性などの運用特性によって決める
共有モジュールの移行もよくある課題
綺麗に分割できれば良いが、難しければ共有ライブラリ (JARDLLgem など) に抽出
共有は結合の一形態なので、マイクロサービスでは避けられる
モジュール自体を複製する方法もある
サービス分割を特定した後は、UI とビジネスロジックを分離
UI で情報を統合して表示する必要があるため、UI がしばしばモノリスに引き戻す → UI を分離しておく
呼び出しポイントの修正
新しい統合点が変更されないよう、適応度関数を構築し、コンシューマ駆動契約を追加
進化可能アーキテクチャ構築の手引き
の進化は、古い層 (呼吸を無意識に行うような) の上に新しい層を積み重ねてきた
大企業のソフトウェアアーキテクチャも同様
現代の DevOps は、スノーフレーク (スノーフレークサーバー) をイミュータブルインフラストラクチャに置き換えることで、動的平衡の問題を局所的に解決
DevOps のベストプラクティスの多くは、可逆可能な判断 (元に戻す必要のある決定) を可能にする
未知の未知はソフトウェアシステムの元凶 → 予測可能性ではなく進化可能性を選べ
BDUF が苦しむのもこれが理由
変化から守る方法に腐敗防止層がある
アジャイルアーキテクトは、意思決定における最終責任時点原則を重んじる
nobuoka 意思決定はできるだけ延期させる、というものっぽい
Fred Brooks が 『人月の神話』 で語った 「Plan to Throw One Away (何を捨てるかを計画すること)」
セカンドシステムシンドローム : 小さく賢明なシステムが、巨大な怪物に進化する傾向
外部への依存に対して、開発者は利益を意識していても、制約・コストについては無視しがち
我々のコードがライブラリを呼び出すのに対して、フレームワークは我々のコードを呼び出す
フレームワークはアプリケーションのコードの基盤なので、バージョンアップに速やかに追従していく必要
外部依存関係に 2 つの指定を導入することを提案
不安定な依存関係 : 自動的に新しいバージョンに更新
保護された依存関係
エンドポイントへのバージョン付け
ナンバリング : エンドポイントにバージョン番号を含めるような形 (新しいバージョンは新しいエンドポイント)
内部解決 : 呼び出し元は同じエンドポイントを呼び、呼ばれた側が適切なバージョンを選択
nobuoka 具体的にどうやるんだろ

7 章 進化的アーキテクチャの落とし穴とアンチパターン
技術アーキテクチャについて
ベンダーキングアンチパターンを避けるために、ベンダー製品は単なる統合点として扱う
統合点の間に腐敗防止層を設ける
抽象化が望ましくない方法で欠如しないよう、適応度関数で保護する
漸進的な変更について
ビジネス上の関心事
製品のカスタマイズは落とし穴のひとつ
カスタマイズはコストがかかるので、進化を妨げる
だからといってカスタマイズ可能な製品の構築を禁止すべきではないが、関連するコストの現実的な評価が必要
レポート機能はアンチパターンになりやすい
レポート機能のために直接 DB を参照することは、進化を妨げる
広い計画範囲は落とし穴
少ない情報で多くの判断や仮説が行われる
仮説に費やされるコストが多くなるほど、その仮説に執着する (埋没費用効果、あるいはコンコルド効果)
→ 選択肢を開いたままにする方法を考えること

8 章 進化的アーキテクチャの実践
ソフトウェアアーキテクチャの影響は広範囲 → チーム編成や予算編成にも影響
ドメイン中心チームは機能横断型チームになる傾向
ドメインを中心にアーキテクチャとチームをモデリングすることで、共通の変更の単位が同じチームにより処理される → 人為的な摩擦が減少
チームの重点を移すための方法の一つが、プロジェクトではなくプロダクトを取り巻く環境をモデル化すること
プロダクトチームは、プロダクトの長期的な品質に所有責任を持つことになる
コンポーネント間の結合をコンシューマ駆動契約により保護する
チームがうまくいくかどうかはチームの人数ではなくチームが維持すべき人のつながりの数に依存
→ 小さな機能横断型のチームが良い
アーキテクトはチーム構造がアーキテクチャの結合特性に与える影響も考慮すべし
うまく機能するアーキテクトは、リーダーシップを発揮し、技術文化を作り、進化的アーキテクチャ構築に必要なスキルを個々のエンジニアに伝えていく
実験文化が重要
小さく実験 (技術的にもプロダクト的にも) し、うまくいったものを既存のシステムへと統合する
様々な方法
スパイクソリューション
Google20 % ルールAtlassianShiplt など、イノベーションのための時間を確保
セットベース開発に従い、短い期間 (数日未満) でいくつかのアプローチをプロトタイプする
エンジニアとエンドユーザーをつなげる (A/B テストなど)
アーキテクチャの量子数と量子当たりのコストは、量子が増えるほど減少する (スイートスポットに達するまで)
進化的アーキテクチャにおいて、エンタープライズアーキテクトの役割は手引き企業規模の適応度関数が中心
アーキテクチャ内の意図的な結合点とプラットフォームの選択についての手引き
既存のアーキテクチャに進化可能性を付け加えるためにどこから始めるか?
低い位置にぶらさがった果実
効果のあるもっとも簡単な問題を最初に
モジュール性を高めて結合を分離することで、適応度関数と漸進的な変更という進化的アーキテクチャの側面を実証
シナリオ前後のメトリクスの収集が必要 : 「実証が議論を打ち負かす」 ということわざもある
nobuoka 原文どんなんだろ
日本語だと 「論より証拠」 やな
最大限の価値あるものを最初に
テストの導入
進化的アーキテクチャの漸進的な変更の側面において、テストは重要
開発と運用の壁を取り払う
実際に運用されているアプリケーションに接することができない状態で、開発者が混乱をほどくのは難しい
進化的アーキテクチャの将来の展望
AI を使った適応度関数
なぜ進化的アーキテクチャに取り組むか
企業は予測可能性を評価する (戦略的な問題の長期的な計画) が、それが有効でない世界になっているため
ソフトウェア開発における動的平衡は予測可能性の効力を失わせた
イノベーションのジレンマにより、十分に確立された市場の企業も失敗する可能性
スケールさせるため
従来、アーキテクチャのベストプラクティスはリレーショナルデータベースをバックエンドにしたトランザクションシステムを構築し、データベースの多くの機能を使用して調整を行うことだった
このアプローチの問題はスケーリング
多くの耐ビザンチン故障技術がこの問題に対処したが、それは根本的な問題解決ではない
高度なビジネス機能を実現するため
A/B テストなどで、ユーザーをフィードバックループに組み込むため
ビジネスメトリクスとしてのサイクルタイムの改善のため
サイクルタイムはいくつかの市場では差別化要因になっている
大規模で保守的な企業では、ソフトウェアをオーバーヘッドとしてとらえ、コストを最小限に抑えようとする
イノベーティブな企業では、ソフトウェアを競争上の優位性とみなす
競争の激しい市場の企業は、サイクルタイムを第一級のビジネスメトリクスとする
量子レベルでアーキテクチャ特性を分離するため
進化的アーキテクチャの構築を決断すべきでないのはどういうときか
実現可能性が低いとき (再構築した方がコストがかからないとき)
既存のシステムのモジュール性を見出して、それを中心に再構築できるかを考える
進化可能性を捨ててでも実現すべきアーキテクチャの特性がある場合
ドメイン固有アーキテクチャは 1 つの特性を最大化するようなアーキテクチャ群
実用最小限の製品を作成するときなど
ビジネスをまもなく閉じる予定であるとき
進化的アーキテクチャを実施すべきことを他者に説得ことに苦労 → 説得よりも実証
痛みがある場所を直していく