$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

進化的アーキテクチャ読んだ

最近アーキテクチャ関連の知識を身につけようと思い、進化的アーキテクチャを読んだ。

言葉の定義が独特で、正直この本は難しいな〜と思った。勉強になったなと思うことは、変更しやすいアーキテクチャを作るための流れの部分。自分は以下のように作ると解釈した。

  • そのアーキテクチャが対象とする領域で、進化しても保護したい重要な特性を特定する
    • 例: スケーラビリティ、監査可能性、即応性など
  • それぞれの特性に対して適応度関数を定義する
    • 適応度関数とは、あるアーキテクチャ特性がどの程度満たされているかを評価する客観的な指標
    • たとえば即応性を、全レスポンスの95%tileのmsで指標化するみたいなイメージ
  • 定量指標がどの値を超えたら望ましくないかを定義し、開発フローに組み込む
    • たとえば全レスポンスの95%tileを500ms以内にする、平均1sを超えるエンドポイントを作ってはならないなど
    • 指標化したら、自動で計測できるようにしたり、継続的に観察できる場所に置いておいたりする。それにより変更があったとしても重要な特性が守られ続けていることを確認する

あとは以下の文章が刺さったかな。気をつけたい。

ほとんどの開発者は有用なものを作るためにフレームワークを使うよりも、フレームワークを作る方を好む。メタ作業はただの作業よりも面白いから。楽しいメタ作業であるという理由だけでアーキテクチャを構築してはならない

読書ノート

- ソフトウェアアーキテクチャづくりには、要件だけでなく、パフォーマンスや監査可能性などのあらゆる関心ごとが釣り合う解決策を探らなければならない 265
    - 関心ごとは、システムを構築する際の「何を」や「どうやって」といった判断をひっくり返す可能性があるもの
- アーキテクチャ作りの関心ごとに、進化可能性を付け加えたいということが本書で言いたいこと 282
- 昔はソフトウェアの変更が高価だったためアーキテクチャは変えにくいものと定義されたが、DevOpsなどの開発プラクティスの進歩によって、その前提を無効化した。一方変更が難しいという立場を取るならば今でも変更が難しくなってしまう。変更しやすさをアーキテクチャの基盤原理に組み込めれば、変更はもはや困難でない 387
- 進化的アーキテクチャの定義:進化的アーキテクチャとは、複数の次元にわたる漸進的で誘導的な変更を支援するものである 418
- 適応度関数を使い、アーキテクチャとエコシステムが時間と共に進化していく中でも、重要な特性を保護する 505
    - 気づき:パフォーマンス目標はSLOによって保護されているみたいなものが適応度関数の事例になりそう
- 適応度関数とは、あるアーキテクチャ特性がどの程度満たされているかを評価する客観的な指標 656 ☆
- 適応度関数は、3つの単純なカテゴリに分類できる 846
    - 主要なもの:技術や設計を選択する上で重要なもの。これらの要素を中心とした変更を容易に行えるようにする設計上の選択を探ることに、より多くの労力をさかなくてはならない。例えば、銀行アプリケーションなら、パフォーマンスと回復力が主要な次元となる
    - 関連性のあるもの:機能レベルで考慮する必要があるものだが、アーキテクチャ上の選択を誘導することはない。例えば、コードベースの品質に関するコードメトリクスは重要だが、主要な次元ではない
    - 関連性のないもの:設計と技術の選択に影響を与えないもの
    - 気づき:適応度関数がどれに所属するかは対応する事業ドメインによって異なる
    - 主要な適応度関数と関連性のある適応度関数を適応した結果は、共有スペースなどで可視化し続ける必要がある。それによりその知識を保ち続けられる ☆
- 漸進的な変更 950
    - 気づき:この辺は「モノリスからマイクロサービスへ」の方がわかりやすい
- 進化的アーキテクチャ構築の流れ 2494
    - 進化しても保護したい次元を特定する
        - 〜性のリストを参照 282
    - それぞれの次元に対して適応度関数を定義する
        - 気づき:つまり定量指標を定義するということ
    - 定量指標がどの値を超えたら望ましくないかを定義し、デプロイメントパイプラインに組み込む
- ほとんどの開発者は有用なものを作るためにフレームワークを使うよりも、フレームワークを作る方を好む。メタ作業はただの作業よりも面白いから。楽しいメタ作業であるという理由だけでアーキテクチャを構築してはならない 2642 ☆

より少なく、しかしより良く - 「エッセンシャル思考」読んだ

自分がなんでもやりたいタイプなので、この本に書いてあることは中々刺さった。幸福になるには「より少なく、しかしより良く」を追求すべきという本。プライベートや仕事でとにかく忙しく時間がないと思っている人は読んでみると良い。

印象に残ったのは次のことだ。

  • 現代人の最優先課題は、優先順位づけの能力をキープすること
    • 睡眠不足では一番最初にそこが減ってしまうのでダメ
    • 一流のバイオリニストは1日平均8.6時間の睡眠 & 週平均2.8時間の昼寝。睡眠による並外れた集中力で、1時間あたりの練習効果を最大限にする
  • もっとも厳しい基準でやることを決める
    • 「絶対やりたい」「やらない」の2択にする。やろうかな程度なら却下、イエスと言うのは絶対やるしかないと確信した時だけ
    • 自分の中で最重要基準をひとつ用意し、100点満点で90点以上取った時だけOKとする
  • やたらと手を出して後から調整しようとすると、どうしようもなくなって不本意に手放すことになる
    • 反射的に手を出すのをやめて何もしないことを選ぼう。たとえばチャットなどで議論が起きている時に反射的に返信ボタンを押さない

とにかく自分が必要と思ったより多く寝て、絶対やりたいことだけ手を出し、反射的に口出すのはやめましょうっていう学びを得た。

読書ノート

- エッセンシャル思考とは、「より少なく、しかしより良く」を追求する生き方 19
- 本当に重要な物事を見極める5つのやり方 77
    - じっくりと考える余裕、情報を集める時間、遊び心、十分な睡眠、何を選ぶかという厳密な基準
- 一流のバイオリニストは1日平均8.6時間の睡眠 & 週平均2.8時間の昼寝 116 ☆
    - 睡眠による並外れた集中力で、1時間あたりの練習効果を最大限にする
- 現代人の最優先課題は、優先順位づけの能力をキープすること 120
    - 睡眠不足では一番最初にそこが減ってしまうのでダメ
- 最も厳しい基準で決める 122 ☆
    - 「絶対やりたい」「やらない」の2択にする
    - やろうかな程度なら却下、イエスと言うのは絶対やるしかないと確信した時だけ
    - 自分の中で最重要基準をひとつ用意し、100点満点で90点以上取った時だけOKとする 124
- 本質的な目標を決めるための問いは、「たったひとつのことしかできないとしたら、何をするか?」 149 ☆
    - 細かい言葉にこだわらない。バズワードに引きずられる恐れ
    - 具体的でわかりやすい言い方にはしよう。「達成をどうやって判定するのか?」の質問に答えられることが大事
- 上手に断るコツ 160
    - 判断を友人関係などの関係性と切り離す
    - ノートいう直接的な言葉を使わずに断る 164
        - とりあえず黙る
        - 代替案を出す
        - 予定を確認して折り返す。一旦時間を置くと断ることが容易になる
        - どの仕事を後回しにするか逆質問する
    - トレードオフに目を向ける。ここでイエスと言ったら自分は何を失うのか ☆
- 上手に手放すテクニック 173
    - まだこれを持っていないとしたら手に入れるのにいくら払うか?まだこのチャンスが手に入っていなかったら、手に入れるためにどれだけのコストを払うか?
    - 何かを言う前に5秒まつ。反射的に言わずに「これは本当に重要か?」を考える
    - 逆プロトタイプ。ひとまずやめてみて誰が困るか様子を見る
- やたらと手を出して後から整理しようとするとどうしようもなくなって不本意な削除を選ぶことになる 188
    - 反射的に手を出すのをやめて何もしないことを選ぼう
    - たとえばチャットなどで議論が起きている時に反射的に返信ボタンを押さない

優れたテストスイートの4本の柱を学ぶ - 「単体テストの考え方、使い方」を読んだ

良いテストケースの作成手法を学ぶ - 「はじめて学ぶソフトウェアのテスト技法」を読んだ - $shibayu36->blog;に引き続き、ソフトウェアテストの知識について言語化を進めたいと考え、「単体テストの考え方、使い方」を読んだ。

この本では優れたテストスイートの4本の柱を「退行に対する保護」「リファクタリングへの耐性」「迅速なフィードバック」「保守しやすさ」と定義し、これらの観点で優れたテストスイートを作る方法について教えてくれる。またこの4つの柱はトレードオフの関係にあるため、単体テスト・統合テスト・E2Eテストがそれぞれどの観点を重視すべきかなどについても言語化してくれている。

自分はこの本は非常に勉強になった。なぜなら単体テスト・統合テストの指針が明快に記述されていて理解しやすく、また行きすぎたテスタブルな設計を諌め、テストしやすいがシンプルな設計についても教えてくれるからだ。

この本の中で以下の部分が印象に残った。とくに何をモックすべきかという部分については非常に面白かった。

  • テストメソッド名の指針は、ドメインエキスパートにどういう検証をするのかが伝わるような名前を付けること
    • 検証内容が明確に分かる名前をテストメソッドにつける
    • 例) Delivery_with_a_past_date_is_invalid()。過去の日付が指定された配達は不正である
  • テストは1単位の観察可能な振る舞いを検証する
    • 観察可能な振る舞いとは、「クライアントが目標を達成するために使う公開された操作」「クライアントが目標を達成するために使う公開された状態」のどちらかのみ
    • 1単位の振る舞いなので、assertが1つである必要はない
  • 「退行に対する保護」を最大限に持つように、モックの適用は管理下にない依存のみにすべき
    • 管理下にない依存 = アプリケーションが好きなようにできない依存。管理下にない依存とのコミュニケーションは外部からも見られる。例: メールサービス
    • そのアプリケーションのみから使われるデータベースなどはとくにモックする必要はない。逆にモックしてしまうとテストが通っても本番で動く保証が少なくなってしまう
    • 管理下にない依存とアプリケーションの境界に近い場所をモックするほど、退行に対する保護を最大限に備えられる。できる限りシステムの境界に位置するものをモックする

読書メモ

- 単体テストで成し遂げたいのはプロジェクトの成長を持続可能なものにする 7
- コードは資産ではなく負債。コードが増えればバグが持ち込まれる経路が増え、維持コストが高くなる 10
- coverageが高いほど良いテストではない。coverageは悪いテストスイートであるかを判断するのみ 11
- 単体テストにおける少量のコードが意味するもの は、単一のクラス、単一のクラスのメソッド 37
- テスト分離の観点の違い 41 🌟
    - 気づき: 古典学派の共有依存のみの置き換えをすべきというのがこの本の趣旨
- 単体テストで複数のAAAがあるのはおかしい 61
    - 統合テストなら好ましい場合がある。実行時間の関係で
- メソッド名に結果も書いてあるけどなあ 63、74
- Actフェーズが一行を超える場合、きちんと設計されていない可能性 63
- 単体テストで検証するのは、1単位のコードではなく、1単位の振る舞い。なのでassertが一つである必要はない 65 🌟
- いわゆるfixture使うな理由がちゃんと書いてある 75
- 何を検証するのかを明確に説明する名前をテストメソッドにつける 77
    - テストメソッドをドメインエキスパートからもわかるように
- テストメソッド名の指針 79 ⭐️
    - 厳格な命名規則に縛られない
    - ドメインエキスパートに、どういう検証をするのかが伝わるような名前をつける
    - 気づき: BDD推奨っぽい 81
- パラメータ化テストはgoっぽい 83
    - パラメータ化テストは正常系と異常系を分けた方が良い 86
- 優れたテストスイートの特徴 95 🌟
    -  4本の柱
    - 退行に対する保護。バグに対する検知度
    - リファクタリングへの耐性
    - 迅速なフィードバック
    - 保守しやすさ
- 振る舞いをテストすることでリファクタリング耐性を上げる 98
- テストコードがテスト対象の実装の詳細を検証するのはダメ 104
    - 最終的な結果を確認する 106
    - 役割の違いの良い図 ⭐️
- モックとスタブは外部に向かうコミュニケーションの模倣か、内部に向かうコミュニケーションの模倣か 133
    - この辺りにモックとスタブの違いが細かく記されている
- スタブのやり取りを検証しない 135
- モックとスタブの両方の性質を持ったテスト・ダブル 137
    - コマンドクエリ分離とモックスタブ
- 気づき: テストケースを実装の詳細と結びつけないことがとにかく大事。何度も言っている 140
- テストすべき観察可能な振る舞いとは、次のどちらか 141 ⭐️
    - クライアントが目標を達成するために使う公開された操作
    - クライアントが目標を達成するために使う公開された状態
- 理想とすべきAPIの設計は、いかなる目標であれ、1つの操作で目標を達成できる 146
    - 気づき: これは確かにそういう印象あり
- 気づき: テストの中で置き換える依存かどうかの判定は、複数のテストケースをランダム・同時に実行した時に落ちうるか 162
- 気づき: 置き換える必要のないプロセス外依存の理由について知れば、データベースでもモック化しない理由がわかる 162
- 単体テストの3つの手法 ⭐️ 168
    - 出力値ベース・テスト:戻り値を確認する
        - 副作用がない場合のみ適用できる
    - 状態ベース・テスト:状態を確認する
        - 状態とは、テスト対象システムの状態、協力者オブジェクトの状態、データベースやファイルシステムなどのプロセス外依存の状態 170
    - コミュニケーション・ベース・テスト:オブジェクト間のやりとりを確認する
- 単体テスト手法と、リファクタリング耐性・保守性の関係性 179
- 隠れた入出力の例 183
    - 隠れた出力:副作用、例外
    - 隠れた入力:内部もしくは外部の状態への参照
- 気づき: 関数型アーキテクチャの場合、出力値ベース・テストがしやすいのはその通り 189
    - 気づき: Goについては例外を基本使わないので、隠れた入出力のうちの一部が起こらない。そのためパラメータ化テストが基本になったのかも知れない
- 4種類のプロダクション・コード。ドメインにおける重要性と協力者オブジェクトの数で分類 217
    - コントローラ = 複数のコンポーネントが適切に連携できるような調整をする
    - 過度に複雑なコードは、ドメイン・モデルとコントローラに分割するほうが良い 218
- 質素なオブジェクトを作り、テストしづらい依存とロジックと分離する 220
- 事前条件についてテストするのは、事前条件がドメインにとって重要な時だけ 239
    - 従業員の数がマイナスにならない例は重要な事前条件、data.Length >= 3みたいなやつはそうではない
- 単体テストと統合テストのテストしたい領域の違い 263
- プロセス外依存をモックに置き換える基準 269 🌟
    - 管理下にある依存ならモックしない、管理下にない依存ならモックする
    - 管理下にある依存 = アプリケーションが好きにできるプロセス外依存
        - 例: そのアプリしか参照しないデータベース
    - 管理下にない依存 = アプリケーションが好きなようにできない依存。管理下にない依存とのコミュニケーションは外部からも見れる
        - 例: メール・サービス
- 統合テストの指針は、すべてのプロセス外依存を経由してビジネスシナリオを正常に終わらせるハッピーパスと、単体テストで検証できない異常系 275 🌟
- 実装クラスが一つしかない場合、プロセス外依存をモックにする必要がない限りはインターフェースは必要ない 282
- バックエンドのシステムは大抵のケースでドメイン層、アプリケーションサービス層、インフラ層の3つで十分 286
- 循環参照を防ぐために、ドメインからは値を返し、上の層で調整するやり方がある 289
    - CheckOutServiceはReportオブジェクトを返し、実際にReportをするのは上の層
    - 気づき: これはミスにつながる設計だと思うし、本当にいいのかなあという気はする
- 管理下にない依存とアプリケーションの境界に近い場所をモックするほど、退行に対する保護を最大限に備えられる 313 🌟
- モックのベストプラクティス 320
    - モックの適用は管理下にない依存のみに
    - モックの置き換え対象はシステムの境界に位置するものに
    - モックの利用は統合テストに限定
    - モックに対して行われた呼び出しの回数を常に確認
        - 想定する呼び出し、想定していない呼び出しがないことの二つを検証するため
    - モックの対象になる型は自身のプロジェクトが所有する型のみに
        - 気づき: これが良い理由があんまりわからんな〜
        - 気づき: 必要な機能のみを公開できると良いので、アダプタがあると便利というのは分かる