「ソフトウェアアーキテクチャメトリクス」を読んだ感想

## 書籍について

[ソフトウェアアーキテクチャメトリクス ― アーキテクチャ品質を改善する10のアドバイス](https://www.oreilly.co.jp/books/9784814400607/)  

本書は10名のソフトウェアアーキテクトが、実際に現場で取り組んできた「アーキテクチャの実践禄」のような書籍です。  
全10章構成で1名1章ずつ自身の経験や取り組みを執筆されています。そのため1章から生真面目に読む必要はなく、気になった章を読み進めて問題ありません。  
全体を通して、アーキテクトの方々が実際に向き合ってきた取り組みが記載されているので、教科書タイプの書籍とは異なりエッセイ感覚で読み進めることができました。  
新しい概念を学ぶというよりかは「アーキテクトの取り組み」を直に感じられるものです。  

以下に1〜10章の個人的に印象に残った部分と感想、最後に全体を通しての感想を記載しました。  
## 1章 解き放たれた4つのキーメトリクス
[LeanとDevOpsの科学](https://tatsu-zine.com/books/accelerate) で一躍有名になったいわゆるFourKeysの実践例です。  
私自身はFourKeysを用いての開発生産性可視化のような取り組みを実践したことがないので、単純な読み物感覚で読み進めました。  

FourKeysとはその名の通り4つのキーとなるメトリクスのことで、ざっくり言うとこのメトリクスが優れている企業は業績が良いとか、そのような研究結果も提示されているメトリクスになります。（詳細は上述の「LeanとDebOpsの科学」をご参照ください。）  
4つのキーメトリクスはさらに二つに分類され、「開発生産性を測る指標」が二つと「サービスの安定性を測る指標」が二つあります。  
詳細は以下です。

- 開発生産性を測る指標
  - デプロイの頻度
    - パイプラインを通って時間経過においてどれほどデプロイが実行されたかを示す
  - 変更のリードタイム
      - 完成したコードや設定変更が、パイプラインを通ってデプロイされるまでにかかる時間
- サービスの安定性を測る指標
    - 変更時の障害率
        - デプロイされた変更のうち、サービスに障害を引き起こした割合
    - サービス復旧時間
        - サービスに障害が発生してから、その障害に気づき修正されるまでの時間
        
この章を読んで特に参考になったのは「FourKeysを観測する場合は全てのメトリクスを同じスコープで計測しなければ意味がない」という点です。  
例えば「デプロイ頻度は特定のマイクロサービスのみ記録しているが、障害はサービス全体で計測している」などでは「開発生産性に対するサービスの安定性」が正しく評価できず、メトリクスを元に思案するサービス改善の施策が正しく機能しない恐れがあるということです。  
また、「サービスの安定性を測る指標」を収集する上で「サービスの障害」の定義についての記述も印象的でした。  
「仮に障害があったとしても、ユーザが誰もその事実に気づいていないのであればそれは障害と言えるのか」「動作はしているが著しく性能を損なう改修がデプロイされ、それがユーザの意欲を削ぐのであればそれは障害ではないか」などの問いかけがあり、定義はなんでも構わないので「自分が納得のいく定義を定めてそれに忠実に従いましょう」とのことでした。  
「自分が納得のいく定義」とありますがこれは一開発メンバーが納得したところであまり意味はない、というか収集するのが難しいものです。  
「開発生産性を測る指標」は、BizとDevが組織的に分割されている場合でも、Devチームのメンバー間だけ収集可能ではありますが、「サービスの安定性を測る指標」については組織的な取り組みが必要ではないでしょうか。  
そもそもこの4つのキーメトリクスの数値がどうのこうのというより、これらを定義し、収集し、継続した改善に繋げていく、という動きに障害がない組織である時点で、かなり意識が高く優れたエンジニアリングを提供できるのではないだろうか...とサイロ化された組織に長年いた身としてはそう感じました。

## 2章 適応度関数ピラミッド:アーキテクチャテストとメトリクスのためのアナロジー
[進化的アーキテクチャ](https://www.oreilly.co.jp/books/9784873118567/) で提唱され、「[ソフトウェアアーキテクチャの基礎](https://www.oreilly.co.jp/books/9784873119823/) 」「[ソフトウェアアーキテクチャ・ハードパーツ](https://www.oreilly.co.jp/books/9784814400065/) 」などでも繰り返し触れられている「適応度関数」について、定義の仕方や具体的な実践例を交えて説明されている章です。  
進化的アーキテクチャを読むだけでは、ふわっとした概念レベルでの理解だった「適応度関数」についての解像度があがり、大分と現場での実践イメージが沸きました。  

適応度関数の実装方法はさまざまで、プロダクトのテストコードに混ぜ込めるものもあれば、別途メトリクスを収集して閾値を判定するアラートのようなものも含まれます。  
この章では、どのような区分で適応度関数を定義・分類し、テストコードのテストピラミッドのように適応度関数ピラミッドを視覚化することで、バランスよく適応度関数を配置し、アーキテクチャ特性を守りながらプロダクトを成長させるための勘所が示されています。  

具体的には必須と任意の二つにわけ、以下の区分が示されています。  

#### 必須の区分
- 規模
    - アトミック/ホリスティック
        - アトミックとは、システムの限られた部分のみの検証を指す
            - テストカバレッジの検証やOSSにCVEが含まれていないかの検証など
        - ホリスティックとは、システム全体の検証
            - ホリスティックかどうかは「その検証の範囲がシステムにとってどれだけ重要か」によって判断される
            - 目安として後述の「品質特性」が複数のものをカバーしている適応度関数である場合はホリスティック足り得る可能性が高い
- 実行契機
    - トリガー式/継続的/手動
        - トリガー式とは、なんらかのタイミングで自動実行されるものを指す
            - 具体的には特定のbranchにpushされた際のCI/CDなど
        - 継続的とは、特定の契機で実行するというよりかは、常時メトリクスを収集の上で検証されるものを指す
            - 私の実践例でいうと、[goreplay](https://github.com/buger/goreplay) を用いて本番環境へのリクエストを一定数転送するテスト環境を作り、その環境を評価する、というものが該当する
        - 手動は、その名の通り手動での実行
            - リリース前に手動で実行する負荷試験・外部に依頼する脆弱性テストなど
            - 可能な限り自動化されることが望ましいが、実現に異常にコストがかかったりそもそも不可能だったりといったものもあるため、手動も十分選択肢になりえる
- 実行場所
    - パイプライン/テスト環境/本番環境
        - CI上で実行するか、どこかの環境にデプロイされたアプリケーションを評価するか
            - 本番環境での適応度関数の例としてはカオスエンジニアリングやメトリクスを用いてのアラートなどが含まれる
- メトリクスの種類
    - 真偽値/離散値/時系列・履歴値
        - 「テストカバレッジが90%を超えているか」の適応度関数であれば「テストカバレッジ > 0.9」を満たすかの真偽値
        - 「100rpsを99%tile50ms以内で返す」という適応度関数であれば、99%tileのレイテンシという離散値のメトリクスを収集する必要がある
- 品質特性
    - ISOの品質特性など
        - トレードオフスライダーなどで定義してプロダクトの品質特性の何を担保したい適応度関数なのかを示す
        - 必然的にトレードオフスライダーで重要とした特性の適応度関数が優先的に定義されるため、プロダクトとしてのアーキテクチャ特性が設計時から一貫して担保されようとしているのかの相関が見える

#### 任意の区分
- 一時的か永続的か
    - 永続とは「永久」ではなく「終了日が定まっていない」の意
        - アプリケーションの機能と同様に、開発が進む上で変更・破棄されることもある
    - 基本的には永続だが、長期にわたるリファクタやリアーキのような計画の際に利用される適応度関数などは一時的なものとして定義し、役目を終えたタイミングで破棄する
- 静的か動的か
    - 検証したい値が一律で定められる静的なものか、条件によって変動する動的なものかを示す
    - 基本的には静的なものが多いという理解だが、パフォーマンスの例でいうと「アクティブユーザが1000なら50ms,10000なら100ms以内にレスポンス」のようなものが動的の適応度関数となる
        - 動的な適応度関数は実装が難しいが、その分より具体的なシステムのシチュエーションに合わせて実装できるため、強力な適応度関数となる
- 対象オーディエンスは誰か
    - 開発者・運用者・PdMが最も多いオーディエンスではあるが、システム全体の売上高やアクティブユーザ数の適応度関数など、より広範囲に影響を与える場合のオーディエンスは多様なステークホルダとなり得る
- どこで適用されるか
    - ざっくりいうとマイクロサービス化されているシステムでの適応範囲

適応度関数については他章でも繰り返し登場します。参考までに私の適応度関数に対する理解を後述のまとめに記載しております。    

## 3章 進化的アーキテクチャ:テスト容易性とデプロイ可能性でアーキテクチャを導く
6ページの短い章です。内容はタイトルの通りで、テスト駆動開発と継続的デリバリーの重要性を説いています。  
ほとんどの商用サービスは、初回リリースで完結しているソフトウェアとして提供されることはなく、漸進的な変更を繰り返していくものです。  
そのため、漸進的な変更を容易にする「テスト容易性」と「デプロイ可能性」は、まずどの特性よりも優先して確保すべきである、と述べられています。  
デプロイ可能性について学びになった点は「デプロイメントパイプラインでテスト可能性を担保するのであれば **パイプラインが正常に通過したのであればデプロイ可能であるという状態でなければならない** 」という点です。  
私の今まで関わったプロダクトでは、デプロイを実施するパイプラインはありましたが、mainブランチがマージされた後にビルドが完了したとしても、featureによっては特定のリリース要件を満たさなければリリースできないということも多くありました。(依存するFEやBEのリリース日を待たなければならない。お偉方のリリース承認を待たなければならない。など)  
後に依存関係などは機能トグルを利用するなどして回避してデプロイ可能性を高める施策を取り入れましたが、リリース承認など組織的な部分はなんともできないこともありました。(LeanとDevOpsの科学でそんなもの大して意味がないと言われていた気がしますが...)  
意味のない承認はさておき、「QAやデザイナーによる確認がデプロイ前に必須」というプロダクトも多いのではないでしょうか。  
どこまで自動化できるのかはプロダクトの特性次第ではありますが、UIの確認であればスナップショットテストを入れたり、カナリアリリースによるカナリアの監視と異常時のロールバックの仕組みを自動化するなどによって、デプロイによるシステムへの影響を下げることでパイプライン上だけでデプロイ可能性を担保する道に近づくと考えます。  

デプロイの阻害になるものがあらかじめ入り込んでいる場合は、それが漸進的な変更の大きな妨げになるので何よりも優先してここの整備を行うべき、というのはなるほどなと感じました。  
本章著者であるDave Farley氏は「[継続的デリバリー 信頼できるソフトウエアリリースのためのビルド・テスト・デプロイメントの自動化](https://www.amazon.co.jp/dp/B074BQQ96X) 」「[継続的デリバリーのソフトウェア工学　もっと早く、もっと良いソフトウェアを作るための秘訣](https://www.amazon.co.jp/dp/B0BP9JRZS8) 」の著者でもあります。  

## 4章 モジュール性成熟度でアーキテクチャを改善する
モジュール性成熟度指数（Modularity Maturity Index:MMI）を用いて技術的負債を計測する方法が述べられています。  
聞いたことのない指数ですが、これは現状の技術的負債がどのレベルなのかを把握し、それに伴って「リプレースするのかリファクタリングするのか」を判断するための指標となるそうです。  
MMIでは既存システムを「モジュール性」「階層性」「パターン一貫性」によって評価し、それぞれの値から最終的な指数を算出します。  
私自身は実践していませんし、誤って伝えるのも問題だと思うので、算出方法などは特に記載しません。  
本章著者である Carola Lilienthal氏 [登壇のYoutube](https://www.youtube.com/watch?v=SH-GvVUjONU) がありますのでこちらなどを参照いただければと思います。  
ちなみにこのMMIというのは、本章著者が認知科学の多くの論文と多くのアーキテクチャの評価結果から作成したソフトウェアの評価方法のようです。    

本章では既存の技術的負債を計測する手法として説明されていましたが、システムに関わらずこの指数を計測して一定を下回らないことを適応度関数として定義することで「モジュール性」を担保できると思うので、機会があれば実践してみたいと思っています。  

## 5章 プライベートビルドとメトリクス:DevOps移行を乗り越えるためのツール
私が一番理解に苦しんだ章です。  
というのも、「プライベートビルド」というもののイメージがあまり沸きませんでした。   
 
ざっくりな内容の一例としては  
- マイクロサービス化されているシステムを開発
- サービスAで機能改修を行い、パイプラインも正常に通りデプロイ可能
- デプロイ後にフロントエンドを通して確認したら不具合を発見
- 開発時にフロントエンドを通したプライベートビルドで改修範囲を確認したら気づけてたことだよ  

というものになっています。  
...。  
確かにその通りではあるのですが、開発時に依存関係のあるFEや他マイクロサービスを通した確認を実施する、というのはいささか難しいのではないだろうか。というのが感想です。  
この辺りは私が直近サイロ化されたような組織に属していることもあり、チームトポロジーでいうストリームアラインドがうまく機能しているような組織であれば現実味があるものなのでしょうか。  
余談ですがこのプライベートビルドについてはあまりに理解ができなかったため、Forkwell様主催の[Forkwell Library #44](https://www.youtube.com/watch?v=C52rYX_E9bA) にてslidoを通して訳者の島田さんに質問させていただきました。  
「マイクロサービス化されてCI/CDを通すのが当たり前になると、それによってテストされるべきだ、という固定観念のようなものがついてくるが、それによって損なわれている品質があるのであればプライベートビルドという古くは皆がやっていた方法も選択肢になる」というようなご回答をいただきました。  
このご回答のおかげである程度腑に落ちました。この場を借りて島田さんにお礼申し上げます。ありがとうございます。  

## 6章 組織のスケーリング:ソフトウェアアーキテクチャの中心的役割
アーキテクチャ・ハードパーツのようなストーリー仕立ての章です。  
サービス拡大のためにマイクロサービス化を取り組んだものの、分散モノリスとなってしまったサービスに対して、最近ジョインしたエンジニアであるAnnaが立て直していくという話です。  
イベントストーミングによるドメイン領域のモデリング、KPIやサービスレベル目標を設定することでシステム成長戦略を組織のビジョンとマッチしたものに導いていく、といった取り組みがなされています。  
Annaはまるでなろう系異世界主人公のような破竹の活躍ですが、システムを成功に導くソフトウェアアーキテクトには、これくらい組織を跨いだ行動力は必要なんだろうな、と感じます。  
まず一番にAnnaが申し出たのは「全組織と対話が可能となる権限をもらう」という点だったことが何より印象深いです。  
ソフトウェアアーキテクトはソフトウェアを導くためには組織に目を向けなければなりません。  

## 7章 ソフトウェアアーキテクチャにおける計測の役割
ソフトウェアアーキテクチャメトリクス入門、のようだな、という感想です。個人的にはこれが1章だと読みやすいというか入りやすい内容だと思いました。  
外部計測/内部計測・開発/運用のマトリクスから得た計測の使いどころ、計測の手法、品質特性別のメトリクスを扱う上での勘所、架空のケーススタディを踏まえた説明、アンチパターンの紹介、と一連の流れがわかりやすく、実業務ともイメージを紐付けやすい内容でした。  
以下、内容の興味深かった部分の一部紹介です。   

#### 外部計測/内部計測・開発/運用のマトリクスから得た計測の使いどころ
外部計測とは、開発や運用メンバー以外が直接受ける影響を可視化したもので、内部とは逆に開発や運用メンバーが受ける影響を可視化したものです。  
外部品質・内部品質と考えて問題ありません。 

|   |  開発  | 運用 |  
|:---:|:---:|:---:|
|  外部計測  |  ドキュメントの測定結果など  | レイテンシ・MTTRなど |
|  内部計測  |  コードの静的解析結果など  | CPU利用率やDBのインデックス増加推移など |

- 開発 * 外部計測
    - 計測する内容
        - デリバリーサイクルの一番早いタイミングで測定可能
        - 外部設計がPCI/DSSなどの規格を準拠するものになっているかを確認でき、システムの信頼性が高まる
    - 所感
        - メトリクスというより、ドキュメントのチェックリストのフローなどが想定されているように感じた
        - 例であったPCI/DSSの準拠は、2章で語られている「一時的な適応度関数」に該当するのだろうか
        - ここの理解度は個人的には高くない
- 開発 * 内部計測
    - 計測する内容
        - コードの静的解析やモジュール結合度の評価など
        - CI上で計測可能なものが主にここに該当する
    - 所感
        - 私が経験したものでは、テストカバレッジの評価やArchUnitによるモジュール性のテスト、snykによる脆弱性スキャンなどの適応度関数がここに該当すると理解している
        - アトミックかつトリガー式である適応度関数ピラミッドの最下層のものがここに該当しそう
- 運用 * 外部計測
    - 計測する内容
        - 表にも記載したレイテンシ、スループット、MTTRなどのサービス利用者に直接影響を与えるメトリクス
        - 本番環境でこの計測値が悪化していればアラート（オンコールで叩き起こされるレベルのもの）の条件となり得る（レイテンシ悪化とか障害発生とか）
    - 所感
        - 一番わかりやすい
- 運用 * 内部計測
    - 計測する内容
        - CPU利用率・メモリ使用量など、増加が直接サービスに影響を与えるわけではなく、サービスの今後の問題を予測する上で役にたつ指標
    - 所感
        - ここが一番何を取得するのか難しい気がする
        - メトリクスを取りすぎても活用できずダッシュボードがごちゃごちゃするだけだし、何を分析するためのどのようなメトリクスがあると良いのか、という深い知識が一番必要な項目であると感じた
        - 10章で述べられているGQMアプローチが活きそう

私が以前担当したシステムでは「CPU利用率が90%を超えたらオンコールアラート」という監視を設定されたシステムがありましたが、「CPUがいくら高かろうとシステムがレスポンス返せてるならいいじゃん！電話やめて！」とメンバーを説得の上でオンコールは止めたことを思い出しました。  
なんとなく「それっぽさ」で監視項目を設定しているシステムも珍しくないのかもしれませんが、外部計測なのか内部測定なのかはシステム運用する上でも強く意識することでメトリクスの正しい活用方法に紐づくものだな、と再確認しました。  

## 8章 メトリクスからエンジニアへの進化
ソフトウェアアーキテクチャの書籍を多く執筆されているNealFord氏の章です。  
適応度関数の説明に終始した内容となっていました。  
いくつかの適応度関数の実践例をあげており、特にArchUnitによるレイヤードアーキテクチャの構造を守るテストや循環参照を検知するテストはわかりやすく実践しやすいものです。（進化的アーキテクチャではJDependで記述されていましたが、こちらがメンテされていないのでArchUnitに更新されています。）  
一方、「マイクロサービスの呼び出しは規約を違反していないか（想定外のクライアントから呼ばれていないか）」の適応度関数を自作する、といった内容もあるのですが、私の携わっていたシステムから考えるとこれは過剰というか運用が辛いものである印象を受けました。  
もちろんこれはあくまで「自作する適応度関数のケーススタディ」としてあげている一例なので、「マイクロサービス間の通信の監視する適応度関数は絶対作るべき」という話ではありません。  
本章のまとめにも「開発者やチームのことを考えず適応度関数をとにかく導入すれば良いというものではない」旨の記述があります。  
適応度関数という概念は、システムの保護したいアーキテクチャ特性によっては強力に動作しますが、利用方法を間違えていればただの負債にしかなりませんので、2章の区分を意識して何を守るためにどういう方法で適応度関数を組み込むのか、が重要であると感じます。  

## 9章 ソフトウェアメトリクスを使用して保守性を確保する
ここまでに何度か出てきた循環参照や構造侵食の話に特化した章です。  
4章のMMIのように、この章では構造負債指数(Structural Debt Index:SDI)と言う計測方法を利用して、構造の循環参照を検知して分割する必要がある依存関係のリストを取得する方法などが記述されています。  
詳しくは[著者講演のYoutube](https://www.youtube.com/watch?v=smEH_jxospg)を参照してください。  
また、このSDIを適応度関数として活用する際の評価基準なども示されています。  
私自身は循環参照のコンポーネントと正面から戦った経験がないため、どうにも読み物感覚が強くなってしまう章でした。  

## 10章 ゴール・クエスチョン・メトリクスアプローチで未知数を計測する
GQMアプローチを用いて、システムに必要なメトリクスを特定する方法を紹介しています。  
GQMとは「ゴール・クエスチョン・メトリクス」のことです。設定した目標（ゴール）を実現するための確認観点を質問（クエスチョン）として挙げ、その質問に回答可能となるための情報（メトリクス）を定義することで、そのメトリクスの計測結果から「ゴールを満たせているか」や、メトリクスを改善することで「ゴールに近づいているか」を判断ことが可能となる手法です。  
以下は具体例です。  

| ゴール | クエスチョン | メトリクス |
|:---:|:---:|:---:|
| APIサービスを安定稼働させたい | サービスがレスポンスするエラー率は？ | HTTPレスポンスステータスコード |
|  | 発生した障害の内容は？ | 障害チケットのバグ分類 |  
|  | etc...  |  |  

上記ではクエスチョンとメトリクスが1対1になっていますが、一つのクエスチョンに複数のメトリクスが必要なこともありますし、逆に一つのメトリクスが複数のクエスチョンの解になることもあります。  
このGQMアプローチは一人で実践しても効果的なフレームワークではありますが、可能であればステークホルダを集めてブレインストーミングで実施することで、より多角的で効果を発揮するメトリクスを定義できます。  

本章では具体的なケーススタディも含めてGQMについて記載されております。  
GQMの本質としては、取得するメトリクスに対して **なぜ** を定義する部分にあると感じています。  
「 **なぜ** メトリクスを取得しているのか」「メトリクスが期待した値でない場合に何を損ねているのか」が明確になります。（メトリクスが悪い原因はわかりません。メトリクスが教えてくれるのは「何かがおかしい」ということだけです。）  
著書の中ではあくまで「ゴール -> クエスチョン -> メトリクス」で定義する手法にのみ触れられていましたが、私としては既存サービスでなんとなく取得しているメトリクスや監視項目に対して逆説的に「メトリクス -> クエスチョン -> ゴール」を導き出すことにも利用できそうだな。と感じました。  
そして「なぜ取得しているか不明なメトリクス」が明らかになった瞬間、そのメトリクスは役割を終えてダッシュボードから削除することが望ましいでしょう。  
メトリクスもコード同様に負債となります。とりあえずとっているメトリクスはYAGNIの原則を無視したコードと同じようなものです。  


## まとめ
全体を通して、まさに「適応度関数の実践例」と言える書籍だなと感じました。
今更ながら「適応度関数とはなんぞや」ということについて少し触れたいと思います。     
本書の中にも度々出てきましたが「適応度関数」とは「非機能要件のテスト」に言い換えられると思います。  
それすなわち「品質特性のテスト」でもあり、「アジャイル品質パターン」の取り組みそのものが「適応度関数」という概念に近いと私は理解しています。  
ですので全く新しい概念というわけではなく、今まで実践されていた品質への取り組みに対して、少し新たな視点を提供するというのが「適応度関数」だと考えます。