はじめに
今回の本はこちら。
脳の働きを理解することで、効率的にスキルを向上できる!?
自分の学習方法はいつも非効率的だと思うことが多いので読んでみようと思いました。
少しでも何かが変わるきっかけになったらいいな。
読書メモ
Part 1 コードをよりよく読むために
Chapter 1 コーディング中の混乱を紐解く
コーディングに影響を与える、さまざまな認知プロセス
-
知識不足
長期記憶の問題 -
情報不足
短期記憶の問題 -
処理能力の不足
ワーキングメモリの問題
Chapter 2 コードを速読する
プログラマーの時間のうち、60% は コードを理解する時間 に費やされている
→ ここのスキルを上げると、プログラミングそのもののスキルを大幅に向上できる
コードを読むとき短期記憶と長期記憶の両方を使用する
- 短期記憶は、今読んだばかりの情報を記憶に留めるために利用される
- 長期記憶に情報があれば、短期記憶に保持できなかった情報を補完することができる
- 短期記憶は、30秒以上情報を保持できない
- 短期記憶の容量は、2~6個の要素を保持できる程度
- 短期記憶のサイズを大きくする確実な方法は見つかっていない
記憶のサイズ制限を克服する
- 長期記憶に多くの知識があると記憶がしやすい
- 短期記憶と長期記憶が協調して情報を保持することが大事
読めるコードよりも見えるコードのほうが多い
- 視覚からの情報はアイコニックメモリという感覚記憶を介して短期記憶に送られる
- チャンク化しやすいコードを書く
- デザインパターンを利用する
- コメントを書く
- ビーコンを残す
Chapter 3 プログラミング言語の文法を素早く習得する方法
なぜプログラミング言語の文法知識が重要なのか?
- 記憶している知識が、いかに効率よくコードを読み、理解できるかに大きく影響するから
- 割り込みによる作業の中断が、思った以上に作業の妨げになるから
フラッシュカードを使って文法を素早く覚える
- 片面に説明文、もう片面に対応するコードを書く
- フラッシュカードを追加するタイミング
- プログラミング言語やフレームワーク、ライブラリを学び始めた際、出会った新しい概念に対して追加する
- ある概念を検索しようとしたとき
物忘れを防ぐには
- 長い期間をかけて勉強したほうが記憶に残る
- 月に1度フラッシュカードを見直す
文法を長く記憶に留めるために
- 積極的に思い出そうとする(想起練習)
- 新しい知識を既存の記憶と積極的に結びつける(推敲)
Chapter 4 複雑なコードの読み方
ワーキングメモリとは
ワーキングメモリは
- 脳の思考力
- 発想力
- 問題解決
を担う役割を持つ
ワーキングメモリの定義は、「問題の処理に用いられる短期記憶」である
短期記憶とワーキングメモリの違いを簡単にまとめると
- 短期記憶:電話番号などを一時的に覚える
- ワーキングメモリ:計算など、情報を操作しながら扱う
といったイメージになる
ワーキングメモリでは、一度に扱える情報が2〜6個程度に限られており、これが「認知的負荷」の原因となる
認知的負荷を軽減するテクニック
1. 認知的リファクタリング
コードの保守性よりも、現時点での読みやすさを優先するアプローチを取る
- あいまいなメソッド名がある場合は、インライン化してしまう
- メソッドの定義と呼び出し位置を近づけることで理解しやすくする
理解を深めるために、コードを読む専用のブランチを作成するのも効果的
2. 使い慣れない言語構造を置き換える
慣れていない記述が出てきた場合は、より親しみやすい形に置き換える
例:
-
map→for文 - 三項演算子
? :→if文
そのコードが読みやすいかどうかは、読む人によって大きく異なることがある
自分にとって理解しやすい形に変換して読むことは、決して恥ずかしいことではない
また、同義だが表現の異なるコードは、フラッシュカードを作成して整理しておくとよい
ワーキングメモリをサポートするツール
認知的負荷を感じたときには、外部ツールを使って記憶を補助する
依存関係グラフ
コードの構造を手書きで可視化する
- 変数を丸で囲み、同じまたは関連する変数同士を線でつなぐ
- メソッドや関数を丸で囲み、呼び出しと定義場所を線でつなぐ
- クラスのインスタンスを丸で囲み、クラス定義とインスタンスを線でつなぐ
状態遷移表
値の変化を追いかけるために、メモを取りながら整理する
Part 2 コードについて考える
Chapter 5 コードの深い理解に到達する
変数名は理解の道しるべ
変数に適切な名前をつけることは、コードをより深く理解するための道しるべとなる
変数の多くは、以下の11個の役割に分類できる
- 固定値
- ステッパー
- フラグ
- ウォーカー
- 直近の値の保持者
- 最も重要な値の保持者
- 収集者
- コンテナ
- フォロワー
- オーガナイザー
- テンポラリ
変数の役割を意識して命名することで、コードの意図や動きを直感的に理解しやすくなる
人間がコードを理解する4つの段階
コードを深く理解する過程には、次の4段階がある
- フォーカルポイント(注目すべき箇所)を見つける
- フォーカルポイントから知識を拡張していく
- 関連する要素を通じて、コードに使われている概念を理解する
- 複数の要素を横断して、さらに抽象度の高い概念を理解する
この段階を意識しながらコードを読むことで、表面的な理解から一歩踏み込んだ理解へと進むことができる
コードリーディングにおける現実
平均的なプログラマーは、勤務時間の約60%をコードを読むことに費やしているとされている
プログラミング能力においては、言語能力が特に大きな影響を与える
文章を読む力が、コードを読む力に直結する
コード理解を深めるための読解戦略
文章読解と同様に、コード読解にも有効な戦略が存在する
-
活性化
コード中で知らない概念に出会ったら、その場で調べて学習する -
監視
コードをプリントアウトし、理解できた箇所と理解できない箇所にそれぞれ印をつける -
重要性の判断
コードの中で重要な行を見極め、マークをつける -
推論
変数名やコードの文脈から意味を推測する -
可視化
変数が関与するすべての操作をリストアップし、可視化する -
自問自答
コードについて「なぜこうなっているのか」と自問しながら読む -
要約
コード全体の要約を作成し、コードの意図や流れを把握する
これらの戦略を意識的に取り入れることで、コードに対する理解の深さが飛躍的に高まる
Chapter 6 プログラミングに関する問題をよりうまく解決するには
コードについて考えるためにモデルを利用する
モデルを利用することの利点
- プログラムに関する情報を他人に伝えやすくなる
- 問題解決時の認知的負荷を軽減できる
メンタルモデル
- 長期記憶に保持するために、フラッシュカードを使って記憶の訓練をする
- ワーキングメモリで形成するために、積極的に視覚化を行う
想定マシン
想定マシンとは、コンピュータが何をしているかを考えるための抽象的な表現
プログラミングの概念を説明したり、理解を深めたりする際に役立つ
Chapter 7 誤認識:思考に潜むバグ
新しい言語を学ぶときに起きる勘違い
プログラミング言語ごとに、さまざまな概念が異なるため、誤解が生まれやすい
新しい言語の学習を容易にする方法
類似点と相違点を意識して学習することがポイント
概念変化
誤認識を、新たに学ぶ言語に適したメンタルモデルに置き換えていくプロセス
誤認識の抑制
- 自分の理解が正しいと確信していても、間違っている可能性を意識する
- よくある誤認識をまとめたチェックリストを活用する
- 常に疑いながら学習を進める
誤認識を発見する方法
- ペアプログラミング、モブプログラミングを活用する
- 検証用のテストコードを追加する
Part 3 よりよいコードを書くために
Chapter 8 よりよい命名を行う方法
なぜ名前が重要なのか?
- 名前はコードの大部分を占める
- コードレビューの指摘の約25%は命名に関するもの
- コード中のコメントや名前は「ドキュメント」であり、読み手への手がかり(ビーコン)となる
命名に対するさまざまな考え方
- よい名前は文法を定義できる
- 名前は一貫性を保つ必要がある
コーディング中に命名を完璧にしようとしない
- 識別子の品質はプログラム開発の初期段階で定着しやすい
→ 書き続けていっても、命名の癖はなかなか改善されない - コーディング中は、命名について考え、その品質を向上させるタイミングとしては、適切ではない
→ コードレビュー時など落ち着いたタイミングで振り返る - よくない名前とバグがある箇所が同じであることが多いが、相関性はない
→ 不適切な名前は、勘違いを引き起こしやすい
よりよい変数名のための3ステップモデル
- 名前に含めるべき概念を選ぶ
- 各概念を表す単語を選ぶ
- それらの単語を組み合わせて命名する
Chapter 9 汚いコードとそれによる認知的負荷を避けるための2つのフレームワーク
コードが認知的負荷を引き起こす2つの原因
- コードが構造的にわかりにくい
- コードが内容的にわかりにくい
コードの臭いとは?
- 非常に長いメソッド
- 多機能すぎるクラス
- 複雑すぎるswitch文 など
メソッドレベルのコードの臭い
- 長く、複数の機能を持つメソッド
- 多すぎるパラメータを取るメソッド
クラスレベルのコードの臭い
- あまりにも多機能な巨大クラス
- 機能が少なすぎて抽象化の意味を持たないクラス
コードベースレベルのコードの臭い
- コードベース内に似たようなコードが複数存在する
コードの臭いの影響
- エラーを含む可能性が高い
- 将来的に変更される可能性も高い
コードの臭いが認知に与える悪影響
- 長すぎるパラメータリストや複雑なswitch文
→ ワーキングメモリの容量オーバー - 神クラスや長すぎるメソッド
→ 効率的なチャンク化が難しくなる - コードクローン
→ 間違ったチャンク化を引き起こす
2つのフレームワーク
- 構造的なアンチパターンと概念的なアンチパターン(コードの臭い)
- 言語的アンチパターン(命名や記述のズレ)
言語的アンチパターンの例
- 変数名から想起されるものと中身が違う
- メソッド名から想起される動きと実際の動きが違う
Chapter 10 複雑な問題をより上手に解決するために
問題解決とは
状態空間(考えうるすべてのステップ)を、できるだけ少ない手順でゴールに到達できるように遷移していくこと
記憶の種類
- 手続き記憶(潜在記憶)
-
宣言的記憶(顕在記憶)
- エピソード記憶(個人的な経験)
- 意味記憶(事実や知識)
問題解決能力を高める2つのテクニック
1. プログラミング関連スキルの自動化(潜在記憶の強化)
- タッチタイピング
- キーボードショートカット
- 覚えたいスキルを少しずつ変えて何回も使う
2. 他の人の問題解決方法を研究する(顕在記憶の強化)
- 同僚とコードリーディングやペア作業を行う
- GitHubでコードを探検する
- ソースコードに関する本や記事を読む
Part4 コーディングにおける共同作業
Chapter 11 コードを書くという行為
コード作業の脳への負担
- 検索
- コード内を調べ、特定の情報を探す作業
- 短期記憶に負担をかける
- [負荷軽減方法] 検索したステップを書き留めておくなど、メモを活用する
- 理解
- コードを読んで実行し、その機能を把握すること
- ワーキングメモリに負担をかける
- [負荷軽減方法] リファクタリング、コードのモデル図を書き起こす
- 転写
- 「単にコードを書く」という活動(新しく機能を追加するなど)
- 長期記憶に負担をかける
- 増強
- 検索と理解、転写をすべて組み合わせたもの
- 新しい機能を追加するなど
- [負荷軽減方法] 小さなサブタスクに分割する
- 探索
- スケッチを描くようにコードを書くこと
- 特にワーキングメモリに負担がかかる
- [負荷軽減方法] 設計の方向性や決定事項を大まかにメモする
開発効率を下げる割り込み
- 開発者の時間の 20% は、割り込みの作業に費やされている
- 作業が中断されたあと、コードの編集を再開できるまでに 約25分 かかる
割り込みに備える
- メンタルモデルを保存する
- メンタルモデルの一部を、コードとは別に記録する
- コードにコメントを残す
- メンタルモデルに関するメモ
- コードで表現できなかった情報
- 展望記憶を補助する
- やろうと思っていることをコードに TODO 項目として追加する
- 下位目標のラベル付
- 問題を小さなステップに分割し、それをテキストとして記述する
Chapter 12 より大きなシステムの設計と改善
コードベースの認知特性(CDCB: cognitive dimensions of code bases)
コードを使いやすく保つには、その認知特性を理解し改善することが重要
- エラーの発生しやすさ:動的型付け言語や不明確な命名によってバグが生じやすい
- 一貫性:命名規則や記法が統一されていないと理解が困難になる
- 拡散性:機能が広範囲に分散し、大きなメソッドが認知負荷を増大させる
- 隠れた依存関係:明示されていない依存関係は理解と保守を妨げる
- 暫定性:思考しながらコードを書きやすいか。過度な型チェックは妨げとなる
- 粘性:変更の難しさ。ビルド時間が長いと粘性が高まる
- 段階的評価:コードを部分的に実行・確認できるか
- 役割表現力:変数や関数の目的が直感的に理解できるか
- マッピングの近接度:コードとビジネス領域の対応関係の明確さ
- ハードな心的操作:コード理解に必要な記憶負荷が過度に高い状態
- 副次的表記:コメントや命名などの補助的な情報の活用
- 抽象化:コードの抽象的な操作の容易さ
- 視認性:システム全体や特定箇所の見やすさ
改善方法とトレードオフ
- 特性を評価し、目的に応じた改善策(設計上の処置)を実施する
- ただし、ある特性の改善が他の特性を悪化させることがある(例:粘性を下げるとエラーが増加する)
コード特性が開発活動に与える影響
各活動と関連する特性:
- 検索:隠れた依存関係は障害となり、副次的表記は助けとなる
- 理解:視認性と役割表現力の高さが重要
- 転写(再利用・模倣):一貫性が高いと学習は容易だが、実装時の負荷は増える
- 増強(新機能追加):ビジネス領域との近接度と粘性が影響する
- 探索(新設計の模索):暫定性と段階的評価が高いと容易になり、心的負荷と高い抽象度は困難をもたらす
活動に合わせた最適化
- 安定ライブラリ → 検索を重視して最適化
- 新アプリ → 増強・転写を重視して設計
Chapter 13 新しい開発者のオンボーディング
オンボーディングプロセスにおける課題
- シニア開発者が新人に一度に多くのことを学ばせようとしてしまう
- シニアにとって「簡単」と感じることでも、新人にとってはそうではないことがあると認識する必要がある
熟達者と初心者の違い
熟達者と初心者はまったく異なる考え方をしており、まったく異なる行動をとる
初心者が熟達者から新しい概念を学ぶ際に陥りやすいアンチパターン
-
ハイフラットライン
抽象的な概念ばかりを伝えてしまい、具体的な理解につながらない -
ローフラットライン
細かすぎる情報を一気に与えてしまい、混乱を招く -
下降専用エレベーター
抽象 → 具体 という一方向の教え方に終始し、最終的な「再抽象化」のフェーズが抜け落ちている
オンボーディングプロセスを改善するためのアプローチ
コードベース上で開発者が行う5つの活動を順に行うことで、新人はタスクをこなしていくことができる
-
探索
コードベース全体をざっと見渡し、構造や関係性を把握する -
検索
特定のインターフェースを実装しているクラスなど、必要なコードを探し出す -
転写
作業の具体的な手順を明示し、実装の計画を新人に提示する -
理解
例えば、あるメソッドの内容を自然言語で説明させるなど、コードの意味を深く理解させる -
増強
新たな機能の追加や既存クラスの拡張を行う。ここでも事前に計画を立てることが重要
理解は開発タスクよりも歓迎すべきである
- 新人にコードを理解させたい場合、実装を任せるよりも理解だけを目的としたタスクを割り当てたほうが効果的な場合がある
- チームで一緒にコードを読み、意味や構造を共有することも有効な方法である
おわりに
認知科学の視点からプログラミングを考察するという、非常に興味深い一冊でした。
脳の働きを理解し、効率的な学習方法を探求することは、プログラミングだけでなく、あらゆる学習において有益だと感じています。
この本をきっかけにオーディオブックで聴いた「一生頭がよくなり続ける もっとすごい脳の使い方」とも多くの内容がリンクしており、納得感を深めることができました。
これまで、コードに手書きでメモを残すことには、なんとなく抵抗がありました。しかし、この本を読んでからは、コードリーディング用にブランチを切り、理解した内容を積極的にメモするようになりました。すると、翌日の作業が格段に楽になることを実感しています。特に3トラックで開発を進めていると、開発スピードが速く、自分の知らないところで機能追加が進んでいることも少なくありません。だからこそ、「コードを理解する」というタスクに集中する時間を確保することの重要性を再認識しました。
まだまだプログラミングの基礎的な知識が不足していると感じることも多いですが、この本を参考にしながら、少しずつでも成長していきたいと思います。