ソフトウェア設計のトレードオフと誤り――プログラミングの際により良い選択をするには(Tomasz Lelek、Jon Skeet)
書籍情報
- 著者:Tomasz Lelek、Jon Skeet(著), 渋川よしき(訳), 山田智子(訳), 本田健悟(訳), 辻大志郎(訳), 宮永崇史(訳), 小橋昌明(訳), 柏木祥子(訳), 岸本卓也(訳), 後藤玲雄(訳), 棚井龍之介(訳), 原木翔(訳), 山本力世(訳)
- 発行日:2023-05-25
- ISBN:9784814400317
- URL:https://www.oreilly.co.jp//books/9784814400317/
書籍目次
- 献辞
- はじめに
- 1章 イントロダクション
- 1.1 すべての決断とパターンの結果
- 1.1.1 単体テストの決定
- 1.1.2 単体テストと統合テストの割合
- 1.2 デザインパターンと、それらが万能でない理由
- 1.3 アーキテクチャデザインパターンと、それらが万能でない理由
- 1.3.1 スケーラビリティ
- 1.3.2 開発スピード
- 1.3.3 マイクロサービスの複雑さ
- 1.4 まとめ
- 2章 コードの重複は必ずしも悪ではない:コードの重複 vs コードの柔軟性
- 2.1 コードベース間の共通のコードと重複
- 2.1.1 コードの重複を必要とする新しいビジネス要件の追加
- 2.1.2 新しいビジネス要求の実装
- 2.1.3 結果の評価
- 2.2 ライブラリとコードベース間のコードの共有
- 2.2.1 共有ライブラリのトレードオフとデメリットの評価
- 2.2.2 共有ライブラリの作成
- 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章 例外 vs 他のエラーハンドリングパターン
- 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 ホットパスの最適化:コードの性能に影響する決断
- 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.3 依存するライブラリの設定を隠蔽するツール
- 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章 日付と時間のデータを効率よく扱う
- 7.1 日付と時間情報の概念
- 7.1.1 機械時間: 時点、エポック、期間
- 7.1.2 常用時 : 暦法、日付、時間、時間量
- 7.1.3 タイムゾーン、UTC、UTCからのオフセット
- 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.3 ベンダーロックイン
- 9.5.4 ライセンス
- 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.3.1 プロデューサーの一貫性と可用性の選択
- 11.4 コンシューマーの実装とデータ配信セマンティクス
- 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.4 遅延評価と先行評価の使い分け
- 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 まとめ
- 訳者あとがき
- 索引
1章 イントロダクション
[概論]
2章 コードの重複は必ずしも悪ではない:コードの重複 vs コードの柔軟性
[概論]
3章 例外 vs 他のエラーハンドリングパターン
[概論]
4章 柔軟性と複雑性のバランス
[概論]
5章 早すぎる最適化 vs ホットパスの最適化:コードの性能に影響する決断
[概論]
6章 API のわかりやすさ vs メンテナンスコスト
[概論]
7章 日付と時間のデータを効率よく扱う
[概論]
8章 データローカリティとメモリーの活用
[概論]
9章 サードパーティーライブラリ:あなたが使うライブラリはあなたのコードとなる
[概論]
10章 分散システムにおける一貫性と原子性
[概論]
11章 分散システムのデータ配信
[概論]
12章 バージョンと互換性の管理
[概論]
13章 流行を追いかけ続けること vs コードのメンテナンスコスト
[概論]