オライリーっぽい表紙だけどオライリーじゃない本!
ずっとオライリー本かと勘違いしていました...(土下座) 著者はかのGoogleでテックリードを務めているというTim Longさん。若手のソフトウェアエンジニア向けに、プロフェッショナルたちが常に活用している技術の概念を説明し、持続可能で信頼されメンテナンスしやすいコードの書き方を様々な観点から広く解説した本となっています。翻訳はかのLINE社の皆さん。
このフレーズだと国内の著名な商業本ですと『良いコード/悪いコードで学ぶ設計入門』と偶然ながらタイトルが似ているのが面白いですが、根本的に目指すところはだいたい似ていますが別の本となっております。
- オライリーっぽい表紙だけどオライリーじゃない本!
- Chapter 1 コードの品質
- Chapter 2 抽象化レイヤー
- Chapter 3 コードでの契約
- Chapter4 エラー
- Chapter5 コードを読みやすくする
- Chapter6 想定外の事態をなくす
- Chapter7 誤用しにくいコードを書く
- Chapter8 コードをモジュール化する
- Chapter9 コードを再利用、汎用化しやすくする
- Chapter10 ユニットテストの原則
- Chapter11 ユニットテストの実践
- まとめ:2020年代の良いコード設計の教科書
Chapter 1 コードの品質
Part1理論編の最初は一番基本のところ。コードの品質のゴールを示し、6つの柱を示し、コード品質がいかに重要なを改めて論じています。
分かりやすいコード例でお菓子のレシピが出てきたりマルゲリータピザが出てきたりモジュール化された/されていないおもちゃが出てきたり、メタファーの例え話が普遍的で秀悦。
そして最終的には、今は一般的に世の中で論じられている通り、高品質なコードを書くことは短期的には開発速度が落ちるように見えるが、中長期的には開発速度を上げるのだ...となっています。
Chapter 2 抽象化レイヤー
WebアプリケーションのController層-なんとか層-Model層とかDataaccess層...のレイヤーやClean Architectureの玉ねぎ構造のレイヤーよりももっと根本的な、大きな問題→小さな問題、抽象→具象、全体→詳細のレイヤー分けがとても大事だよという話。ここでもHTTPメッセージを送るコードの例がとても秀悦。
オブジェクト指向を使っているといつも出てくる、クラスで関心の分離をしたりインターフェイスを活用したりする話も一通り出てきます。
最後には個々の問題の解決策を個々にデプロイできるよう分割したマイクロサービスも良い方法だが、それでもひとつひとつのマイクロサービス内でも抽象化レイヤーは大事だよ、となっていますね。
Chapter 3 コードでの契約
他人や未来の自分のために、コメントで理解できるようにしておくのは重要だという話。自分でさえ自分のコードをいずれ忘れるという話。そしていくら詳細なドキュメントを作ってもエンジニアは読まない傾向があるなんて話もあり、Googleレベルのエンジニアでさえワイたちと同じなのか...と思ったりします。(笑)
章タイトルから予想される「契約による設計」の話は後半に書いてありますね。
Chapter4 エラー
プログラミング全体を扱っているので伝統的なJavaベースのtry-catchのエラー解決をコード例のメインにしつつ、Rust言語で見られるような先進的なResult型なども扱っています。頑張るとJavaでも同じようなことはやれることはやれるんですね。
JavaのException型の大分類、検査型例外と非検査型例外については双方の主張、メリットやデメリットを論じつつ、この本の作者さんの意見としては検査型例外で明確に処理する方を勧めています。このへん公平で良いですね。
catch節でエラーを握りつぶすのは常にダメ!というは多くの本でも語られてその通りなのですが、本書でもしっかり述べられています。
ドキュメントはいずれメンテされなくなる、プログラマーは関係ないコードの細部まで見ない...などの話もあって、Googleで働くレベルの人たちでもやっぱり(以下同文)と思います。
Chapter5 コードを読みやすくする
次はPart2の実践編。タイトルからすでに2012年の名著『リーダブルコード』と似たようなテーマの章。
いきなりパリティチェックをする、僕も読んでいてよくわからない難しい処理が間に挟まった処理を例に、関数の分割や命名、コード、コメント、諸々を例にしっかり解説していきます。Javaっぽいサンプルコードが様々出てくるんですがこれらも例が秀悦。
2020年代の本らしく無名関数も扱っていて、小さな処理を書くには有益、そして巨大な無名関数は有害になることが多いので名前付き関数に分けていったほうがよいとなっていますね。
Java8で導入されたStream APIを例にとり新しい言語機能を有効に使う話も解説。ここでも新しい方法を使うのは常に最善ではなくて、「目の前の状況に最適なのか常に問いかける」のが大事だと書いてあるのが現実的ですね。
Chapter6 想定外の事態をなくす
古きC言語の時代の-1を返す関数や、Int型の最大値を異常時に返すという謎の関数を例に、こういうマジックバリューがどういう場合に危険な事態を招くのかしっかり書いてあります。
nullを返したりnullオブジェクトを返したり、、空文字、列挙型など諸々解説されています。
入力パラメーターの変更に注意、誤解を招くような関数の書き方を兼ねる話ではサンプルにGUIアプリのサンプルコードが出てきますが、こちらもどういう場合にまずいのか例がよく作られている...!
そしてこれらをすべてテストで解決するのは理想主義的であり、実際はそううまくはいかないとあるのも現実的ですね。
Chapter7 誤用しにくいコードを書く
UI寄りの処理を例に、不変な値を持ったクラスやデザインパターンのBuilderパターンの効果的な使用例など。
あまりに汎用的なデータ型を避けるという例では、常に長さ2想定の経度と緯度を持った数値配列を、さらに配列に入れて持つというバグに脆弱なデータの持ち方を例にとってしっかり解説しています。経験のある方なら読んでいてあっこれ望ましい最終形はアレだな...と分かる通りの結論が出てくるのですが、その過程の解説もしっかりしています。
オブジェクト指向プログラミングで時折論争になるデータオブジェクト(あるいはデータクラス)の話では、OOPの「伝統的な」考え方の支持者には悪いことだと思われている場合もあるが有用である...と書いてあったり、さすが2020年代の本で新しいですね。
後半では「信頼できる唯一の情報源」という、フロントエンド界隈でも時々聞くような気のする言い回しも登場。出てくる例も実際にありそうなコードで、解説がしっかりしていて中立的で良いです。
Chapter8 コードをモジュール化する
モジュールやパッケージでフォルダ分けする話かと思ったらちょっと違って、コードをうまく分割していく話。
- DIの一番簡単なコンストラクタインジェクションでうまい設計をやる話
- オブジェクト指向だと必ず登場、インターフェースに依存せよという話
- 継承の話。ここで継承の問題点、継承より移譲(本書だとコンポジション)でうまくやっていく話。例が分かりやすい。
- カプセル化
- 例外をラップしてその抽象レイヤーにふさわしいエラーを返し、読み手に分かりやすくする話
などなど、オブジェクト指向全般の話題が出てきます。SOLID原則や書籍Cleanシリーズで語られているあのへん、他の設計を扱った書籍でもよく出てくるあたりの話題ですね。
自分的にはちょうど会社の勉強会でオブジェクト指向の話をテーマにやろうと思っていたので、繋がってよかったです。
Chapter9 コードを再利用、汎用化しやすくする
- この関数メソッドの返り値は配列の0番目なんだ...のような読み手に誤解されやすい想定を避けるべく、戻り値や関数名で工夫する話
- グローバル変数は危険!というのはどこでも言われるんですが、ショッピングカートを例に論理的に説明
- デフォルトの戻り値を有効に活用する話。抽象度の高いより上位のレイヤーでやった方がよいという話
- 関数の引数は必要最低限のものだけ渡した方がよい話
- ジェネリクスの
が多くの場合に役立つ話
などの話題。
Chapter10 ユニットテストの原則
TDDやってなくてサバンナ先生スンマセン...と思いつつPart3 ユニットテスト編に突入します。
- お馴染みテストコードによるユニットテストの話。用語の呼び方、流派があったりであることを踏まえつつ公平に解説しています。
- 関数1個1個にとらわれずあくまで機能に対して、実装の詳細に囚われずにテストメソッドを作る話
- パブリックAPI単位でやるべきだが、逆に重要な動作は無視しちゃいけないという話。ここでメタファーに出てくる珈琲自動販売機の例がとても腑に落ちます。
- 外側の世界からテストを守る、逆にテストから外側の世界を守る話。これも図が分かりやすい。
- テストダブルでモック、スタブ、フェイクの話。ここも詳しく書いてあるのですが、自分はどうも差がよくわかりませんでした...前者2つはテストフレームワークが創るものかな?
- 狭義で正しい方のTDDの話題も出てくるのですが、メリットはみな知っているのだが実際に実践しているエンジニアにはそれほど会ったことがありません...と正直に書いてあって、Googleで働くレベルの人でも(以下同文)と思いました。
Chapter11 ユニットテストの実践
- 銀行口座を例に1関数=1テストでは不十分な話
- privateな関数をテストのためだけにpublicにするのは悪手だという話
- 突き詰めるとコードは小さい単位に分けていった方がよいという、コード設計の原則へ。
- 一度にひとつの動作だけをテストしていった方が良い話。テストケースをその分細分化していく。
- テスト用ライブラリのsetup系の処理を有効に使う話。構成の共通化は基本やめる
- マッチャーという仕組みを適切に使う
- コンストラクタ注入でもよいからDIを適切に使う
そして最後は「本書で最も重要なパート」という触れ込みでChapter1に出てきた理解しにくい情報の正体、チョコレートブラウニーのレシピがついてきます。改行がついていて読みやすい!
まとめ:2020年代の良いコード設計の教科書
長い旅が終わって読了しました。たくさんのことが学べますが、そもそもGood CodeとBad Codeの違いは常に明確だとは限らないことも冒頭で述べられています。経験ある多くのエンジニアが知る格言、全ての場合に100%当てはまる万能の銀の弾丸はないこと、トレードオフがあることも、文中で度々述べられています。
文中に沢山出てくる例が秀悦なサンプルコードはだいたい僕の脳にはJavaと解釈されて見えるのですが、null安全確保、名前付き引数も実現済み、Java+Kotlinが合体した理想のオブジェクト指向言語のような疑似コードになっています。
PythonやJavaScriptだと毛色が違うかもしれませんが、メジャーな言語を習得済みの方ならだいたい雰囲気はつかめるかと思います。随所にモダン言語のこれならこの機能、この言語ならこのライブラリが使える...などの解説もあって2020年代の今に即しています。
複数の考え方がある場合は両方を解説した上で著者の意見を述べたり、このへん非常に公平で広い視座を持っていてよいと感じました。
技術書には主張の強い本もあってそれはそれで面白いのですが(ボブおじさんのCleanシリーズとか!笑)、この本はあまり強く主張せずに、静かに良いことを沢山言っているんですね。知識の泉に到達した達人特有の見識の幅広さや謙虚さのような何かを感じて、これがGoogleの人の本か...!と思いました。
サンプルコード以外のメタファーも世の中の普遍的な事柄を扱っていてわかりやすい。日本語への翻訳もかなり自然で読みやすいです。
巻末の訳者あとがきにも良いことが書いてあって、すべての環境に100%当てはまる絶対のルールはなく、常識を働かせつつ自分をアップデートしながら、新しい書籍や議論に参加しながら、自分の前の現実に実践していくのが大事なのだ...とあります。まさにそうなんですよね。この系統の書籍は何冊読んでも自分の血肉になって後々役に立つんですよね。
2010年代に有名になった『リーダブルコード』とどちらを読むべきか...と言ったらもちろん両方なのですが、本書想定の経験3年以内のソフトエンジニアのみならず、経験何年目の人にも役立つ良書でした。
おまけ:本書冒頭に上げられている参考文献
予想通りというべきか、どれもオールタイムで名前が挙がる名著ばかりですね。どれも確実にお勧めです。あっ自分がまだ読めてない本もありました...