『ソフトウェア設計のトレードオフと誤り』
https://gyazo.com/da58b2eb1190d616bee4030c0dd12c59
2023/5/25
サンプルコートはJava
なんだかんだ「それJavaの話でしょ」という内容も多い
章によって当たり外れが大きい感じがある
関心が異なるので当然かも知れないが
7章以外は今じゃないかな、という感想になった
オライリーのサブスクないんかいmrsekut.icon
はじめに
意思決定をメモってたの良いね
1章 イントロダクション
testのためにpublic methodにするやつ
1.2 デザインパターンと、それらが万能でない理由
1.2.1 コードの測定
1.3 アーキテクチャデザインパターンと、それらが万能でない理由
1.3.1 スケーラビリティ
1.3.2 開発スピード
1.3.3 マイクロサービスの複雑さ
1.4 まとめ
2章 コードの重複は必ずしも悪ではない:コードの重複 vs コードの柔軟性
この章のシナリオ
2つのチームが独立したサービスを作っている
決済サービス
個人サービス
その中で独自に認証機能を実装している
認証機能を共通のライブラリとして抽出して利用する
versionを合わせる問題など
ライブラリを辞めてマイクロサービスとして独立させ、HTTP API経由で接続する
ライブラリに比べて疎結合、責任が分離される
クラウドの管理コストがかかる
Java固有の話が多いな
割と読み進めたがいまいち面白くないmrsekut.icon
2.3 異なるマイクロサービスへのコード抽出
2.3.1 分離したサービスのトレードオフとデメリット
2.3.2 サービス分離に関する結論
2.4 コードの重複による疎結合の改善
2.5 重複を減らすための継承を用いたAPI設計
2.5.1 リクエストハンドラーの共通部分の抽出
2.5.2 継承と密結合について深掘りする
2.5.3 継承とコンポジションのトレードオフ
2.5.4 本質的な重複と偶然の重複に着目する
2.6 まとめ
3.1 例外の階層
3.1.1 すべてのエラーを捕捉する vs きめ細やかに処理する
3.2 コードで例外を処理するベストプラクティス
3.2.1 公開APIでの検査例外の処理
3.2.2 公開APIでの非検査例外の処理
3.3 例外処理のアンチパターン
3.3.1 エラー発生時のリソースのクローズ
3.3.2 アプリケーションフローを制御するために例外を使うアンチパターン
3.4 サードパーティーライブラリの例外
3.5 マルチスレッド環境における例外
3.5.1 Promise APIを使った非同期処理の例外
3.6 Tryを使った関数型アプローチにおける例外処理
3.6.1 本番環境でのTryの使い方
3.6.2 Tryと例外を投げるコードの混合
3.7 例外処理の実装におけるパフォーマンスの比較
3.8 まとめ
4章 柔軟性と複雑性のバランス
4.1 ロバストではあるが拡張性のないAPI
4.1.1 新しいコンポーネントを設計する
4.1.2 もっとも簡単なコードから始める
4.2 好きなメトリクスフレームワークを利用できるようにする
4.3 フック経由でAPIに拡張性を与える
4.3.1 フックAPIの想定外な利用への対処
4.3.2 フックAPIの性能への影響
4.4 リスナー経由でAPIに拡張性を与える
4.4.1 リスナーの利用 vs フックの利用
4.4.2 イミュータブルな設計
4.5 APIの柔軟性分析 vs メンテナンスコスト
4.6 まとめ
5章 早すぎる最適化 vs ホットパスの最適化:コードの性能に影響する決断
コードの中で、ほぼすべてのユーザのリクエストに対して実行され、多くの処理を行っている部分
「最適化」という単語はパフォーマンス観点にのみ使う、という前提があるっぽい文章だなmrsekut.icon
5.1 早すぎる最適化が悪である場合
5.1.1 アカウントを処理するパイプラインの作成
5.1.2 誤った仮定を元にした最適化処理
5.1.3 性能の最適化をベンチマークする
5.2 コードの中のホットパス
5.2.1 ソフトウェアシステムにおけるパレートの法則を理解する
5.2.2 SLAに対応する並列ユーザー(スレッド)数を設定する
5.3 ホットパスになりうる部分がある単語サービス
5.3.1 「今日の単語」を取得する
5.3.2 単語が存在するか検証する
5.3.3 HTTPサービスを用いてWordsServiceを公開する
5.4 コードの中のホットパスを検出する
5.4.1 Gatlingを利用した APIの性能テストの作成
5.4.2 MetricRegistryを用いてコードパスを測定する
5.5 ホットパスの性能を改善する
5.5.1 既存の解決策に対してJMHマイクロベンチマークを作成する
5.5.2 キャッシュを用いて単語の存在検証機能を最適化する
5.5.3 性能テストを修正し、入力単語数を増やす
5.6 まとめ
6章 API のわかりやすさ vs メンテナンスコスト
6.1 他のツールから利用されるベースのライブラリ
6.1.1 クラウドサービスクライアントを作る
6.1.2 認証の実現方法を探る
6.1.3 設定のメカニズムを理解する
6.2 依存するライブラリの設定をそのまま公開する
6.2.1 バッチツールの設定
6.3 依存するライブラリの設定を隠蔽するツール
6.3.1 ストリーミングツールの設定
6.4 クラウドクライアントのライブラリに新しい設定を追加する
6.4.1 バッチツールへの新しい設定の追加
6.4.2 ストリーミングツールの設定の追加
6.4.3 UX の使いやすさとメンテナンスのしやすさについての2つの解決策の比較
6.5 クラウドクライアントライブラリの設定を非推奨にする/ 削除する
6.5.1 バッチツールからの設定の削除
6.5.2 ストリーミングツールからの設定の削除
6.5.3 UXの良さとメンテナンスのしやすさにおける2つの解決策の比較
6.6 まとめ
7章 日付と時間のデータを効率よく扱う
この章、めっちゃボリューミー
p.185の訳注おもろmrsekut.icon
日本の選挙日と誕生日の関係
期日前投票と不在者投票で扱いが異なるとか
不在者投票は選挙当日に18歳になってるなら17歳でも投票できる たまに日本語が意味不明な文章がある
日付系のlibrary、絶対作りたくねえな、という気持ちになったmrsekut.icon
7.1.4 私を悩ませる日時の概念
7.2 日時情報を仕事で扱うための準備
7.2.1 スコープを制限する
7.2.2 日付の要求の明確化
7.2.3 適切なライブラリ、パッケージを利用する
7.3 日時のコードの実装
7.3.1 概念を適用し続ける
7.3.2 テスト容易性の改善
7.3.3 日時をテキストとして表現する
7.3.4 コメント付きのコード解説
7.4 認識してテストすべき境界条件
7.4.1 カレンダー計算
7.4.2 夜中のタイムゾーン切り替え
7.4.3 曖昧な時間やスキップされた時間を取り扱う
7.4.4 更新されるタイムゾーンデータの取り扱い
7.5 まとめ
8章 データローカリティとメモリーの活用
8.1 データローカリティとは何か?
8.1.1 計算処理をデータ側に移動する
8.1.2 データローカリティを使った処理のスケーリング
8.2 データのパーティション化とデータ分割
8.2.1 オフラインでのビッグデータのパーティション化
8.2.2 パーティション化 vs シャーディング
8.2.3 パーティション化のアルゴリズム
8.3 複数のパーティションのビッグデータのセットを結合する
8.3.1 同じ物理マシン上にあるデータの結合
8.3.2 データの移動が必要な結合
8.3.3 ブロードキャストを活用した結合の最適化
8.4 データ処理:メモリー vs ディスク
8.4.1 ディスクベース処理を利用
8.4.2 なぜMapReduceが必要なのか?
8.4.3 アクセス時間の計算
8.4.4 RAMベースの処理
8.5 Apache Sparkを使った結合処理の実装
8.5.1 ブロードキャストなしの結合の実装
8.5.2 ブロードキャストを伴う結合の実装
8.6 まとめ
9章 サードパーティーライブラリ:あなたが使うライブラリはあなたのコードとなる
9.1 ライブラリをインポートしてその設定に対して完全な責任を負う:デフォルト値に注意する
9.2 並列実行モデルとスケーラビリティ
9.2.1 非同期、同期 APIを使う
9.2.2 分散時のスケーラビリティ
9.3 テスト容易性
9.3.1 テストライブラリ
9.3.2 偽の値とモック(テストダブル)を使ったテスト
9.3.3 統合テストツールキット
9.4 サードパーティーライブラリの依存関係
9.4.1 バージョン衝突の防止
9.4.2 多過ぎる依存関係
9.5 サードパーティー依存関係の選択と維持
9.5.1 第一印象
9.5.2 コードを再利用する別の方法
9.5.5 ライブラリとフレームワークのどちらにするか
9.5.6 セキュリティとアップデート
9.5.7 意思決定のためのチェックリスト
9.6 まとめ
10章 分散システムにおける一貫性と原子性
10.1 データソースのat-least-once 配信
10.1.1 単一ノード間のトラフィック
10.1.2 アプリケーションからの呼び出しのリトライ
10.1.3 データ生成とべき等性
10.1.4 コマンドクエリ責務分離(CQRS)を理解する
10.2 重複排除ライブラリの素朴な実装
10.3 分散システムで重複排除を実装する際のよくある誤り
10.3.1 ノードが1つの場合
10.3.2 複数ノードのコンテキスト
10.4 レースコンディションを防ぐためにロジックをアトミックにする
10.5 まとめ
11章 分散システムのデータ配信
11.1 イベント駆動アプリケーションのアーキテクチャ
11.2 Apache Kafkaに基づくプロデューサー・コンシューマーアプリケーション
11.2.1 Kafkaをコンシューマー側から見る
11.2.2 Kafkaブローカーの設定を理解する
11.3 プロデューサーの処理内容
11.4 コンシューマーの実装とデータ配信セマンティクス
11.3.1 プロデューサーの一貫性と可用性の選択
11.4.1 コンシューマーの手動コミット
11.4.2 最初または最新のオフセットからの処理再開
11.4.3 (実質)厳密に1回のデータ配信
11.5 データ配信保証の仕組みにより耐障害性を向上させる
11.6 まとめ
12章 バージョンと互換性の管理
12.1 バージョン管理概論
12.1.1 バージョンのプロパティ
12.1.2 後方互換性と前方互換性
12.1.3 セマンティックバージョニング
12.1.4 マーケティングバージョン
12.2 ライブラリのためのバージョン管理
12.2.1 ソース、バイナリ、セマンティック互換性
12.2.2 依存グラフと菱形依存
12.2.3 破壊的変更の取り扱い方
12.2.4 内部だけで使用されるライブラリの管理
12.3 ネットワークAPIのバージョン管理
12.3.1 ネットワークAPI呼び出しのコンテキスト
12.3.2 カスタマーフレンドリーなわかりやすさ
12.3.3 一般的なバージョン管理戦略
12.3.4 バージョン管理に関するさらなる考慮事項
12.4 データストレージのバージョン管理
12.4.1 Protocol Buffersの簡単なイントロダクション
12.4.2 破壊的変更とは何か?
12.4.3 ストレージシステム内のデータマイグレーション
12.4.4 想定外を想定する
12.4.5 APIの分割とストレージ表現
12.4.6 ストレージフォーマットの評価
12.5 まとめ
13章 流行を追いかけ続けること vs コードのメンテナンスコスト
13.1 依存性注入のフレームワークの使いどころ
13.1.1 依存性注入の自作
13.1.2 フレームワークを利用した依存性注入
13.2 リアクティブプログラミングの使いどころ
13.2.1 シングルスレッドでの処理の作成・ブロッキング処理
13.2.2 CompletableFutureの利用
13.2.3 リアクティブによる実装
13.3 関数型プログラミングの使いどころ
13.3.1 非関数型言語での関数型コードの作成
13.3.2 末尾再帰最適化
13.3.3 不変性の活用
13.5 まとめ
付録A データライフサイクルとトレードオフ
A.1 データの表現方式
A.1.1 即値(リテラル)
A.1.2 定数
A.1.3 コマンドライン引数
A.1.4 環境変数
A.1.5 設定ファイル
A.1.6 ダウンロードコンテンツ方式
A.1.7 オンラインデータベース
A.2 トレードオフを考慮する視点
A.2.1 デプロイまでの手順
A.2.2 複数のデータセットのハンドリング
A.2.3 起動時間
A.2.4 セキュリティ
A.3 ハイブリッド方式
A.3.1 複数のデータソースの透過利用
A.3.2 ソースコードの自動生成
A.3.3 設定DSL
A.3.4 設定ファイルをバンドル
A.3.5 ライブリロード
A.3.6 .envファイル
A.4 まとめ
訳者あとがき
索引