「A Philosophy of Software Design」を読んだ
A Philosophy of Software Designを読んだので感想を書きます。
A Philosophy of Software Design
- メディア: ペーパーバック
- この商品を含むブログを見る
日本のAmazonでは売ってなくて電子書籍もなかったのでAmazon.comから購入する必要があって大変面倒でした… Kindleだと辞書を引くのも簡単なので英語の本は電子書籍で読みたいですね。
どんな本か?
ソフトウェアの複雑さを減らすためにどのような設計を行えば良いかについて書かれた本です。
特にモジュール設計でインターフェースが最小限で機能の多いDeep Module(深いモジュール?)になるようにモジュールを設計するという内容が参考になりました。
クラスを小さくするプラクティスが行きすぎると小さなクラスが増えすぎて全体としての複雑性が上がることがあるという記述もあり、クラスを闇雲に分割して小さくするのではなくDeep Moduleになるのかどうかを考えた上で分割するようにする必要があるというのは今後意識していきたいと思います。
全般的に今後設計する際にこの本の各省の内容に立ち返ってどう設計すべきか考えていきたいと思える内容で読んでよかったです。
読書メモ
Chapter2: The Nature of Complexity
- 複雑さとはシステムの理解や修正を難しくするソフトウェアシステムの構造
- 複雑さの症状
- 修正の連鎖
- 一つの修正が複数箇所に連鎖する
- 認知負荷
- コード量の多いアプローチの方が認知不可が低くなるためシンプルになることがある
- 未知の未知
- 3つの症状の中で最悪
- バグが出るまで修正が必要であることに気づくすべがない
- 修正の連鎖
- 複雑さの原因
- 依存
- ソフトウェア設計のゴールは依存の数を減らしできるだけシンプルで明瞭にすること
- 不明瞭さ
- 変数名やドキュメント
- あまりに多くのドキュメントが必要になるのはシステムの設計が正しくない兆候
- 依存
- 複雑さは蓄積される
- 複雑さが蓄積されると一つの依存や不明瞭さを解消するのとは異なり修正が困難になる
Chapter3: Working Code Is'nt Enough
- 良い設計のためには戦術的ではなく戦略的にプログラミングする必要がある
- 戦術的プログラミング
- できるだけ早く機能改修やバグ修正を行うことを目指す
- 複雑さが蓄積され彼機能改修に時間がかかるようになる
- 戦略的プログラミング
- 動くだけのプログラムを目標とせずシステムの設計を改善するために時間を投資する
- 10-20%の時間を投資する
- 最初は遅くなるが時間が経つにつれてその時間は取り返せる
Chapter4: Modules Should Be Deep
- モジュール設計
- 開発者が全体の複雑さの必要な一部分のみに対すれば良いように複雑さを管理する
- モジュール間の依存を最小限にすることが目標
- モジュールをインターフェイス/実装の二つに分けて考える
- 深いモジュール
- インターフェースが最小限で機能が多い
- 良いモジュール
- 浅いモジュール
- 提供する機能に対してインタフェースが複雑
- 深いモジュール
- 「クラスは小さくすべき」という考えが行き過ぎると個々のクラスはシンプルだが全体のインタフェースとして複雑になる
Chapter5: Information Hiding (and Leakage)
- 情報隠蔽
- 深いモジュールを達成するための一番重要なテクニック
- 必要最小限の機能を外部に公開する
- 情報漏れ
- 設計修正の影響が複数のモジュールにおよぶ
- 設計の危険信号
- 時間分割
- システムの構造が実行順序に対応する
- モジュール設計ではタスクの実行順ではなく各タスクを実行するのに必要な知識に集中する
Chapter6: General-Purpose Modules are Deeper
- 汎用/専用のどちらで実装するか
- 少し汎用にするのが良い
- 実装は現在の要求を満たす専用の実装でインターフェースは汎用にする
- 汎用のアプローチは良い情報隠蔽につながる
- 汎用/専用の正しいバランスを見つけるための問い
- 現在の要求を満たす最もシンプルなインタフェースは?
- このメソッドはどのくらいの数の状況で使われる?
- 現在の要求のためにこのAPIを使うのは簡単か?
Chapter7: DIfferent Layer, Different Abstraction
- よく設計されたシステムではソフトウェアの各レイヤーは異なる抽象化を提供する
- パススルーメソッド
- ほぼ同じシグネチャーのメソッドを呼び出すメソッド
- クラス間の責務が綺麗に分離されていない
- 同じシグネチャーが常に悪いわけではない
- 有用で明確な機能を提供するかどうかが重要
- デコレーターパターン
- インターフェースと実装
- 異なる抽象化となる
- 同じ抽象化がされている場合はそのクラスは深くない
- パススルー変数
- 変数をメソッドチェインで引き渡す
- 間のメソッドでその変数を意識しなければいけないため複雑度が増す
Chapter8: Pull Complexity Downwards
- シンプルな実装よりもシンプルなインターフェースが重要
- 設定パラメーターを採用するのは公開するのはできるだけ避ける
- 複雑さを引き下げる際に注意する点
- その複雑さはクラスの機能に関連しているか?
- アプリケーションのシンプル化に寄与するか?
- クラスのインターフェースをシンプルにするか?
Chapter9: Better Together or Better Apart
- 二つの機能を同じ場所で実装するか分離するか
- 目標は全体としての複雑さを下げてモジュール度を上げること
- 関連する機能はまとめた方が良い
- 情報を共有している
- 一緒に使われる
- 概念的に重なっている
- 上のレイヤーの方が専用で下のレイヤーの方が汎用のコードになる傾向がある
- 汎用/専用が混在する場合は専用のロジックを分離して上のレイヤーにする
- メソッドの分離と併合
- 各メソッドが「一つのこと」を「完了させる」ようにすべき
- 各メソッドが単独で理解できるようにする
Chapter10: Define Errors Out Of Existence
- 例外は複雑さにつながる
- 不必要な例外を定義すると例外処理の問題が悪化する
- 例外の多いクラスは複雑なインターフェースを持ち例外の少ないクラスよりも浅くなる
- 例外処理を行う場所を減らす
- 例外をなくす
- 同じ理由で特別な条件をなくすことも有用
- 例外マスク
- システムの下のレイヤーで例外処理を行い、上のレイヤーに例外を伝えない
- 例外の集約
- クラッシュさせる
- 例外をなくす
Chapter11: Design it Twice
- 複数の設計を考えることで良い結果が得られる
- 一つのアプローチが適していると思っていても2番目の設計を考えてみる
Chapter12: Why Write Comments? The Four Excuses
- 正しいコメントは設計を改善する
- コメントを書かない言い訳
- 良いコードはそれ自体がドキュメントだ
- ユーザーがコードを読む必要のあるメソッドは抽象化が無い
- 書く時間がない
- 10%程度しか時間はかからないしかけたコストはすぐに回収できる
- 良いコードはそれ自体がドキュメントだ
- コードに表現されていない設計者の考えを捉えることがコメントの考え方
Chapter13: Comments Should Describe Things that Aren't Obvious from the Code
- コメントはコードで明確になっていないことを書くべき
- コメント規約を決める
- コードと同じ内容を繰り返さない
- 低レベルのコメントは正確さを加え、高レベルのコメントは直感を強化する
- 良い抽象化を表すコードが欲しければ、その抽象化をコメンドに記述する必要がある
- インターフェースのコメントと実装のコメントを分離する
- インターフェースのコメントに実装の内容を混在させない
- 実装のコメントにはhowではなくwhat/whyを書く
Chapter14: Choosing Names
- 良い名前はドキュメントの一種
- 正確で直感的で長すぎない名前をつける
- 命名が難しい場合はそのオブジェクトの設計がクリアになっていない恐れがある
- 名前に一貫性を持つ
Chapter15: Write The Comments First
- 設計プロセスの一部にする
- コメントを最初に書く
- 良いコメントになる
- コメントを書くことで設計が良くなる
- コメントを書くのが楽しくなる
Chapter16: Modifying Existing Code
- 修正が完了した際にその修正が最初から存在していた場合と同じ設計になることが理想
- 設計がベストでない場合はリファクタリングする
- コメントをメンテナンスする
- 記述対象のコードの近くにコメントを書く
- プログラム外にドキュメント済みの情報はリファレンスのみを書く
Chapter17: Consistency
- 一貫性は複雑さを減らし、振る舞いをより明確にする
- 名前
- コーディングスタイル
- インターフェース
- デザインパターン
- 不変性
- 一貫性を保つ
- ドキュメント
- ツールでチェック
- 郷にいれば郷に従え
- 周りのソースに合わせる
- 既存の規約を変更しない
Chapter18: Code Should be Obvious
- 良い名前をつける
- 一貫性
- スペースを適切にいれてみやすくする
- コメント
- コードを不明確にするもの
- イベントドリブンプログラミング
- 汎用コンテナ
- 期待に反する挙動をするコード
Chapter19: Software Trends
- オブジェクト指向プログラミング
- 実装の継承を使う場合はコンポジットなど別の方法で解決できないか検討する
- アジャイル開発
- 戦術的なプログラミングになりがちなので注意
- 単体テスト
- リファクタリングに重要
- TDD
- 良い設計よりも機能が動くことに焦点を当てることが問題
- デザインパターン
- パターンの過適用のリスクがある
- getter/setter
- できるだけ避ける
Chapter20: Designing for Performance
- 重い処理を理解し、軽い処理を選択するようにする
- ネットワーク
- 二次記憶装置へのI/O
- 動的メモリ確保
- キャッシュミス
- 修正する前に測定する
- 原因を探る
- 修正後の基準性能を得る
- クリティカルパスの周辺のコードを設計する
- 特別な状況のコードを除いてシンプルにする
「Apache Hive Essentials」を読んだ
業務でHiveを使う機会が出てきたので、Apache Hive Essentialsを読みました。
オライリーのHive本にするか迷ったんだけど、発行年が古かったので最近発行で良さそうなの探してこれにしました。
- 作者: Edward Capriolo,Dean Wampler,Jason Rutherglen,佐藤直生,嶋内翔,Sky株式会社玉川竜司
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/06/15
- メディア: 大型本
- この商品を含むブログ (3件) を見る
どんな本?
Hiveの概要からインストール、パフォーマンス関連などの一通りの内容が簡潔にわかりやすく説明されていたのでHive初心者が概要を知るのにはよかったです。
英語も平易で読むのにそれほど時間はかかりませんでした。
ただ集約関数などのクエリの例がわかりにくい部分があったのが少し残念でした。(境界値の例が不足していたりで例としてわかりにくかった)
役に立った部分
Performance Considerationの章がクエリプランの見方やファイルのフォーマット、圧縮方法などが説明されていて設計、運用時に役立ちそうです。
またJoinの章の下記も知らなかったので参考になりました。
パフォーマンスとOOM回避のために大きなテーブルをJOINの右端にする方が良い
もしくはヒント句(/*+ STREAMABLE (table_name) */)を使う
逆に集約関数とかクエリとかはSQL知っていれば流し読みで良いかなという内容でした。
200ページ無い薄い本なので概要についてざっと見るには良い本ですが、もう少し内部構造とか知りたい場合はオライリーの本読んだ方が良さそうだと思いました。
Rails5.2.0アップグレード時のマルチプロセスのコネクション管理
Rails5.1.6からRails5.2.0にアップグレードした際に対応が必要な箇所があったので対応内容をまとめます。
事象
アップグレード後にバッチを実行すると毎回同じ箇所でThreadErrorが発生するようになりました。
アップグレード前はバッチ実行時にスレッド数が一定だったのが、アップグレード後はバッチ実行に伴いスレッド数が増加しリソースの上限に達していました。
原因
リリースノート見てもスレッド関連の修正ないし何だろうとしばらく悩んでいましたが、調査したところマルチプロセス時のコネクションの管理をRails側で実行するようになったことが原因でした。
これまでマルチプロセス時のプロセス毎のコネクションはアプリケーション内で用意するコードを書いていましたが、Rails側で面倒を見てくれるようになったため処理が不要になったようです。
対応
修正前は下記のようにプロセス毎のコネクションを用意するコードを書いていました。
config = ActiveRecord::Base.remove_connection Parallel.each(4.times, in_processes: 4) do |i| ActiveRecord::Base.establish_connection(config) begin # 並列処理を実行 ensure ActiveRecord::Base.remove_connection end end
Rails5.2.0ではコネクションの管理が不要なため該当のコードを削除することで、バッチ処理を実行してもスレッド数も増えずエラーが出なくなりました。
Parallel.each(4.times, in_processes: 4) do |i| # 並列処理を実行 end
これで問題なくRails5.2.0にアップグレードできました!
「Rails Developers Meetup 2018: Day 2」に参加した
昨日に引き続き、Rails Developers Meetup 2018: Day 2に参加してきました。
2日目も昨日と同様に現場のリアルな悩みや知見を聞くことができて、とても良かったです。
このような勉強会に無料で参加できるのは、運営、発表者の方々には感謝しか無いですね。次回の開催も楽しみです。
下記所感です。
Observability, Service Meshes and Microservices
@taiki45さんの発表。
マイクロサービスの動向に疎いのでObservabilityやService Meshについての話が参考になりました。昨日に続いてマイクロサービスの話が多いのでやはり大規模になってくるとマイクロサービス化が必要になってくるのですね。
minne での CM 対応でのハイブリッドクラウド運用
@_shiro16さんの発表。
アクセス増加に対して事前に対策を打って障害無く乗り切っているのはすごいですね。
MySQLでJSONフォーマットでEXPLAINすると情報が増えるの知らなかったので使っていきたい。
コードレビュー自動化の最前線から
@soutaroさんの発表。
False Positiveの数が問題ないレベルであれば、プロジェクト固有の問題をLINTツールで検出できるようにするのは良いですね。
業務のアプリだとプロジェクト固有の問題の前に、RuboCopで対応できていないルールも多く残っているのでまずはそちらから対応しないといけない状況ですが。。
知性の習得 - 新人研修内容の一考察
@igaiga555さんの発表。
新人教育において、知性を型と実践に分けて説明されていてとてもわかり易くてよかったです。新人を褒めるとかできていない部分もあるので教える際には意識するようにしたい。
正しく失敗しつつ進むプロダクト開発
@ryopekoさんの発表。
小さく作って失敗しても気づきを最速で得られるようにするというのは、業務の開発でもそうしたいなと思っていて、それをプロセス化して実践できているのはとても良いですね。業務をそのようなプロセスに改善していきたい。
ActiveRecordデータ処理アンチパターン
@toshimaru_eさんの発表。
自分でアンチパターンを踏むことは無くなったけど、レビューで指摘することはあるので、Rails初心者だとあるあるな事例だと思う。
アンチパターンとして名前がついてまとめられていると教えるときにもやりやすくなるので良いですね。
「社内ツール作成サークル」活動記録
@onkさんの発表。
素振りの題材として社内ツールを作るのはとても良さそうだけど、運用していくのが大変そうなので、続けられているのはすごいですね。
やってみたい気持ちはあるけど、業務時間のとり方などの仕組みもないと継続していくのが難しそうですね。。
これからの Ruby on Rails
Railsコミッターの生の声が聞けて面白かったです。Railsコミッターもprintデバッグしているんだと親しみがもてました。笑。
かぶっていて見れなかったセッションで気になるものもあったので動画公開されたら見てみようと思います。
「Rails Developers Meetup 2018: Day 1」に参加した
Rails Developers Meetup 2018: Day 1に参加してきました。
毎回おもしろい話の聞けるRailsdmですが、今回も興味深い話が盛りだくさんで良かったです。
今回はマイクロサービスや設計の話が多く、Railsアプリの規模が大きくなった際の設計の参考になりました。
下記所感です。
安全かつ高速に進めるマイクロサービス化
@k0kubunさんの発表。
内部APIとしてマイクロサービス化するのは大変そうなのでできるだけやりたくないなと思ってしまった。。
リトライ戦略が参考になりました。expeditor gem知らなかったので後で確認したい。
MySQL/InnoDB の裏側
@a_bickyさんのMySQL/InnoDBについての発表。
InnoDBのIndexの仕組みから不要なインデックスを削除する話が参考になった。
Performance Schema知らなかったのできちんと理解して使いこなせるようにしたいですね。
Quipperにおける「関心の分離」の歴史
@kyannyさんの発表。
アプリケーション分割して困った点を共有してもらえるのは参考になるのでありがたいです。「分断されたモノリス」はツライ。。
Rails コントリビューションから学んだGit / GitHub 術
@koicさんの発表。
いつもコミットメッセージはあまり書かずにPRに書けばいいかという感じなのでコミットメッセージちゃんと書こうという気持ちになった。
強い人のコミットメッセージを参考にするのは良さそう。
Elasticsearchによる全文検索の実装
@gfxさんの発表
N-Gramと形態素解析のフィールドを別に持ってソート時に重み付けするのは良さそう。
全文検索ではElasticsearchではなくCloudsearchを使ってるけど、特定のフィールドの値によるスコアリングはCloudsearchでもできるので、もっとチューニングしていきたい。
Realworld Domain Model on Rails
@joker1007さんの設計、DDDについての発表。
- 設計とは概念を発見して名前を付けること
- 設計に必要なのは地図を作ること
- 一度に考えれば良い範囲を限定するための責任境界と集約ルート
- コンテキスト間で連携が発生する場合は依存関係をコントロールする
Railsの設計に悩んでいることもあり参考になるとても良い発表でした。
業務のRailsアプリでもいろいろな機能を負ったServiceクラスや肥大化したクラスが有るので良い設計になるよう頑張っていきたい。
2日目も参加するのでそちらも楽しみです!
「99 Bottles of OOP」を読んだ
「オブジェクト指向設計実践ガイド」の著者のSandi MetzとKatrina Owen著の「99 Bottles of OOP」(https://www.sandimetz.com/99bottles/)を読んだので感想を書きます。
読了するのに数週間かかってしまったので英語の本を読むスピードを上げていきたい。。
どんな本か?
「99 Bottles of Oop」はrubyでオブジェクト指向を学ぶ本で、「オブジェクト指向設計実践ガイド」が各プラクティスごとに説明してあるのと異なり、こちらの本では一つの題材について、実装、リファクタリング、機能追加をTDDで進めていく中でオブジェクト指向での設計、実装の進め方を学ぶことがきます。
概要
各章ごとに重要だと思った点をピックアップします。
1. Redesicovering Simplicity
例題に対して4種類の実装をメトリックなどをもとにどれが一番良いかを検証。
変更が発生するまでは変更容易性よりも理解しやすさに重点をを置いたShameless Greenの手法が良い。
早すぎる抽象化は行わない。
2.Test Driving Shameless Green
Shameless Greenを導くテストをTDDでどのように作るか。
TDDで小さいステップで修正していく。
- テストを書く
- テストを実行する
- 正しく動くよう修正する
3.Unerathing Concepts
新しい要件が発生した場合にどのように修正するか。
開放/閉鎖原則にしたがって、新しい要件を実装する前にコードが「開放」されるまでリファクリングする。
「コードの臭い」や「Flocking Rules」を元にリファクタリングする。
コードの臭いで注目すべきポイント
- 重複
- 大きすぎるクラス
Flocking Rules
- 最も似ている部分を選択
- それらの最小の相違点を見つける
- 差異をなくす簡潔な変更を行う
4. Practicing Horizontal Refactoring
リスコフの置換原則にしたがってリファクタリングを進める。
名前の付け方で抽象的/一般的の尺度を元にどのように決めていくのかが説明されているのが良かったです。
5. Separating Responsibilities
リファクタリングを進め、クラスを責務で分割する。
責務の分割の際は下記の質問を手掛かりにする。
- 同じ形状のメソッドはあるか?
- 同じ名前の引数を取るメソッドはあるか?
- 同じ名前の引数は常に同じ意味か?
- このクラスにprivateを入れるとするとどこに入れるか?
- このクラスを二つの部分に分ける場合どこで分ける?
- 条件分岐のテストに共通点はあるか?
- 条件分岐のブランチ数は?
- 条件分岐以外のコードがメソッドにあるか?
- メソッドが引数以外のクラスの情報に依存しているか?
6. Archeving Openess
データの塊(同時に発生する複数のデータ)をコードから取り除き、条件分岐をポリモーフィズムで取り除く。
データの塊が発生するのはコンセプトが抽出されていないから。
リスコフ違反を修正するのは重要。rubyのような動的型付けのオブジェクト指向言語ではオブジェクト間の暗黙的な契約の中の明示的な信頼に依存しているから。
修正が完了すると新しい要件に対して「開放」されているのでコードの修正を行う。
感想
具体的な例題を元にリファクタリングを進めて良い設計にしていくところが分かりやすくて良かったです。リファクタリングの各手法をどのような観点でどのように適用していくのかが詳細に説明されているので理解しやすかったです。
rubyでオブジェクト指向を学ぶにはかなりおススメの本だと思います。
「オブジェクト指向設計実践ガイド」の原著についても今年第二版がでるみたいなのでそちらも楽しみです。
https://www.amazon.co.jp/dp/0134456475/