コンテンツにスキップする

「良いコード/悪いコードで学ぶ設計入門」を読んだ

投稿時刻2024年1月13日 22:30

良いコード/悪いコードで学ぶ設計入門」を 2,024 年 01 月 13 日に読んだ。

目次

メモ

p8

このような事態はデータを保持するクラスと、データを使って計算するロジックが離れているときに頻発します。
データと計算ロジックがそれぞれ遠く離れた箇所に実装されているために、計算ロジックが複数実装されていても、認知が難しいのです。

このように関連するデータやロジックどうしが分散し、バラバラになっているのを低凝集と言います。
低凝集によって引き起こされる弊害を一度まとめてみましょう。

副作用のデメリット p48

副作用とは、関数が引数を受け取り、戻り値を返す以外に、外部の状態(変数など)を変更することです*1。

もう少し具体的に説明すると、関数(メソッド)には、主作用と副作用があります。
主作用: 関数 (メソッド) が引数を受け取り、値を返すこと。
副作用: 主作用以外に状態変更すること。

p79

デメテルの法則と呼ばれる法則があります。
利用するオブジェクトの内部を知るべきではない、とするもので、「知らない人に話しかけるな」と要約されたりもします。
メソッドチェインで内部詳細を渡り歩くつくりは、まさにデメテルの法則に違反していると言えます。

車輪の再発明 p131

すでに広く使われ確立している技術や解決法が存在しているにもかかわらず、知らないか、または意図的に無視して、新たに同じようなものをつくり出してしまうことを車輪の再発明と呼びます。

anyMatchを使わず自前で実装してしまう例は、まさに車輪の再発明に該当します。

すでに確立している技術を使わないと、新たにつくり出す労力や時間が無駄になってしまいます。
さらに、すでにあるものよりも役に立たないものをつくり出すことを四角い車輪の再発明と呼びます。
実績のあるライブラリがすでにあるのに、知らずに自前で実装し、そのつくりが未熟であるためにバグを発生させてしまうケースが実際にあります。

車輪の再発明による弊害を避けるため、サービス開発においては、フレームワークの機能やライブラリを丁寧に調査することが重要です。

一方で、どんな状況でも車輪の再発明がダメだとは言い切れません。
ライブラリやフレームワークは誰がつくり出しているのでしょうか。
高い技術力を持つエンジニアの方々です。
ライブラリを組み合わせて実装するだけだと、ライブラリがどんなしくみで動作しているかうかがい知ることができません。
つまり技術力がそこで止まってしまうのです。
動作のしくみや根拠を理解するのは、より技術力を高め、開発を豊かにします。
学習目的で、あえて車輪の再発明をやってみるのも一考です。

機能追加とリファクタリングを同時にやらない p315

機能追加とリファクタリングを同時にやってはいけません。
どちらか一方に専念しましょう。

書籍『リファクタリング』(17.1.3)では、この切り替えを「2つの帽子」と表現しています。
作業するときは、「ファンクションの帽子」(機能追加)と「リファクタリングの帽子」どちらか1つだけをかぶっていることを意識しましょう。

この帽子の切り替えを意識しないと、自分が今機能追加しているのか、それともリファクタリングしているのか混乱してしまいます。
またリポジトリへのコミットも機能追加とリファクタリングを分けておかないと、そのコミットが機能追加のためだったのかリファクタリングのためだったのか、後から見分けがつかなくなります。
バグが発生した場合に、機能追加またはリファクタリング、どちらの変更による原因なのか、分析を難しくしてしまいます。

Railsアプリのリファクタリング p316

筆者はリファクタリング専門のエンジニアとして仕事をしています。
「サービスの技術的負債(15.2参照)をなんとか返済して、開発生産性を上げたい」というニーズを受けての仕事です。

現在、筆者は Ruby on Rails (以下Rails) アプリのリファクタリングをしています。

木こりのジレンマ p322

「木こりのジレンマ」という説話があります。

ある木こりが、斧で一生懸命木を切っていました。
通りかかった旅人が木を切る様子をしばらく眺めていましたが、なかなか木が切れません。
よく見てみると、斧が刃こぼれしていたので、旅人は言いました。
「刃を研げば楽に切れますよ」
木こりは答えました。
「刃を研ぐ時間なんかない!」

ソフトウェア開発でも、この「木こりのジレンマ」と同じことが起こっていないでしょうか。
この説話の「木を切る時間」を「ロジックの実装時間」、「刃を研ぐ時間」を「設計する時間」に置き換えて考えてみましょう。
設計していないと、ロジックの変更やデバッグに多大な時間を浪費してしまいます。
そして設計する余裕すらなくなってしまうジレンマに陥ってしまいます。

エンジニアにとっての資産とは何か p324

ちょっと考えてみてください。
「エンジニアにとっての」資産とは何でしょうか。
貯蓄がたくさんあることでしょうか。
それとも高年収であることでしょうか。

人によってお金の使い方は異なるので、貯蓄額の多寡は「エンジニアにとっての」資産とは違うように思えます。
年収に関しても、景気の浮き沈みでいくらでも変わるものですから、「エンジニアにとっての」本質的な資産とは異なっていそうです。

エンジニアにとっての本質的な資産とは何でしょう。
それは技術力にほかならないと筆者は考えます。
貯蓄がゼロでも、技術力があればどこでも稼いでいけるのがエンジニアであり、まさに富を生み出す源泉です。
何にも代えがたい、エンジニア自身の貴重な資産です。

循環的複雜度 p332

循環的複雑度(サイクロマティック複雑度)は、コードの構造的な複雑さを示す指標です。
条件分岐やループ処理が増える、ネストすると複雑さは増大していきます。
複雑さの度合いにより、それぞれ表 15.3 の状態を示すと言われています *8*9*10 。

条件分岐やループ処理、およびネストが複雑さ増大の原因となります。
複雑度は、早期 return やストラテジパターン、ファーストクラスコレクションパターンなど、本書で取り上げたテクニックを駆使することで低減可能です。

データクラスは処理を持たないので複雑度は0ですが、ほかのクラスが複雑化している可能性があるので、注意が必要です。

循環的複雑度は後述する分析ツールにより計測可能です。
ちなみに筆者が設計するクラスの複雑度は、ほとんどが10以内、多くて15以内におさまっています。

p334

人間の記憶は、短期記憶と長期記憶に分類されます。
近年の認知心理学の研究では、人間の短期記憶は一度に4±1個の概念しか把握できない、という説があります。
この個数をマジカルナンバー4と呼びます。
また、記憶個数の単位をチャンクと呼びます。

たとえば英語で「こんにちは」を意味する「Hello」。
これは「H」「E」「L」「L」「0」の5文字から構成されており、「Hello」を初めて学ぶ人にとっては5チャンクになります(なお、繰り返し学習し記憶に定着すると「Hello」がひとまとまりとして記憶され、1チャンクになります)。

プログラミングは、非常に多くの種類のデータやロジックとにらめっこする仕事です。
データや仕様の変更がどこに影響するか、バグが生じないかさまざまな要素を俯瞰し検証することが求められます。

そうした中で、数千、数万行もあり、膨大な変数が使われているような巨大クラスに対し、人は一度にすべての変数やロジックを把握可能でしょうか?
記憶がパンクして混乱してしまうでしょう。
巨大クラスで扱っている概念の個数が、マジカルナンバー4を優に超えてしまっているからです。
実装担当者として長期間携わってきた人にとっては長期記憶として定着し、理解可能かもしれません。
しかし、後任として初めて触れる人にとっては、短期記憶に非常に強い負荷がかかり、理解が難しくなってしまいます。

クラス設計する際は、マジカルナンバー4を援用して、脳にやさしい構造を心がけましょう。
クラス内部で取り扱う概念が4±1個におさまるように設計し、大きなクラスは小さなクラスに分割しましょう。

心理的安全性 p344

チームメンバーとの関係改善には、心理的安全性の向上が不可欠です。
心理的安全性とは、「自分が発言することを恥じたり、拒絶されるなど、不利益を被ることがないことをチームで共有されている心理状態」とか「安心して自由に発言したり、行動できる状態」という定義がなされています。
1999年にハーバード大学で提唱され、2012年にGoogle社の労働改革プロジェクトにて採用されたことから脚光を浴びた概念です。
心理的安全性は、成功に導くチームを構築する上で重要と言われています。

意見や提案をする上で、冷笑されたり、煙たがられたり、聞く耳を持たれない状態では、情報共有がうまくいきません。
ましてチーム単位での設計品質向上はとても難しくなるでしょう。

コミュニケーションに課題があるとき、まず心理的安全性の向上に努めましょう*1。

p347

それでもパフォーマンスが気になるなら、まずは実際にパフォーマンスに影響が出ているか計測しましょう。
計測すると、多くの場合、クラス生成が与える影響はまったく、またはほとんどないことがわかります。

この問題はクラス生成に限りません。
パフォーマンスに対して支配的な部分いわゆるボトルネックは、計測するまでわかりません。
ボトルネックがわからないうちから高速に動作するコードをやみくもに書くことは、早すぎる最適化と呼ばれるアンチパターンです。

パフォーマンスに影響のない箇所で高速化を目指したコードを書き、変更容易性を犠牲にするのは問題です。

p350

ハードルのひとつは、アンカリング効果と呼ばれる心理作用です。
これは、最初に提示された数値や情報が基準になってしまい、その後の判断を歪めてしまう認知バイアスです。
たとえば最初に提示された価格が高額だと、後から提示された価格が割安に感じられます。
最初の価格が損得の判断基準になってしまうのです。
これがアンカリング効果です。
アンカリング効果に振り回されないためには、そもそも最初に提示された価格が本当に妥当だったのかどうかを検証する必要があります。

アンカリング効果はソフトウェア開発でも発生します。
既存のクラス名やメソッド名が基準となってしまい、開発者の判断を歪ませ、混乱させてしまうケースがとても多いのです。
第10章の商品クラスがその例です。
既存の名前に振り回されてしまっては、うまく正体を見破れません。

もうひとつのハードルとして、名前がない、名前を知らないものは知覚が非常に困難であることです。
ジョシュアツリーの法則と呼ばれる法則があります。
これは、名前を知ってはじめて存在を知覚できるようになる、逆に名前を知らないと存在を知覚できないとする認知法則です。

影響力を持つレベルにまで仲間を集める p357

あなたが品質のマズさに気づき、設計のテコ入れを考えたとします。
しかし、自分1人で品質向上活動をしてもなかなか効果は上がりません。
下手をすれば、「あいつは指示以外の何か余計なことをやっているようだぞ」と逆に目をつけられてしまうリスクが生じます。

設計に限った話ではないですが、仕事のやり方をボトムアップで変えていくに周囲の協力が不可欠です。
自分のチームやほかすべてを巻き込めるに越したことはありません。
しかし、中には考えが合わないメンバーがいるなど、多くを巻き込むのは至難の業です。

なぜ協力が必要なのでしょうか。
それは仕事のやり方を変えるだけの影響力を持つためです。
まずは影響力を持てるだけの仲間を集めることが重要です。
影響力を発揮できるだけの規模はどれくらいなのでしょうか。

ランチェスターの法則と呼ばれる軍事理論があります。
これは戦闘力、および敵に与える損害量を定義した理論です。
このランチェスターの法則を応用したマーケットシェア理論は、市場占有率にもとづいた競争理論です。

この理論で注目したいのが、市場占有率別の目標を定義したクープマン目標値です。
目標値のひとつに、影響目標値があります。
これは「影響力を無視できない存在レベルであり、シェア争いに本格参入する目標値」とされています。
この目標値は10.9%と定義されています。
筆者は、この数値が現場の改善のために必要な人数の算出にも活かせると考えます。

10.9%。この数値をエンジニアチームに当てはめて考えてみます。
仮にチームが20人であれば、2.18で約2人。
50人であれば5.45で約5人となります。
それほど多くはありません。
チームが20人なら自分以外のもう1人を仲間にできれば良いのですから、なんだかいけそうな感じがしますね。

まずは気の合うメンバーに声をかけてみましょう。
「最近の実装はどう?変更するのが結構大変だと思わない?」
「変更後のデバッグがつらいよね」
「ちゃんと設計したいよね」など、なんでも良いです。
悩みを打ち明けながら、協力してくれそうな仲間を増やしてみましょう*1。

基本はスモールステップ p358

仲間が見つかると、もしかしたら本書に書かれている内容を一度に伝えてしまいたい衝動に駆られるかもしれません。

しかし、焦りは禁物です。
人は一度に大量の情報を受け止めきれませんし(15.5.5 参照)、大きな変化対して不安や抵抗を覚えるものです。

毎日少しずつ、スモールステップで設計の知識を共有していきましょう。
もちろん聞き手が食いつく、興味がありそうなら、より多くを共有して議論するのはありでしょう。