#book #r2021

# なぜこの本を読んだか
[https://twitter.com/Felienne/status/1425789085118050308?s=20](https://twitter.com/Felienne/status/1425789085118050308?s=20) で始めて知った
著者がプログラミングの理解の研究をしている人で、目次を読んでみると面白そうだったしページ数も250ちょいだったのでサクッと読めそうに思ったのでとりあえず読んでみた
# 何が書かれている本か
プログラマがコードを読むときにどういう脳内構造になっているかを心理学や認知科学の観点から説明した本
John Swller の [[Cognitive Architecture and Instructional Design]] あたりの話題のプログラマ版という感じ
本の内容は以下の4つに大きく分かれている
Part1: On reading code better
- コード読む際のプログラマの脳内構造
- Long term memory, Short term memory, Working memory, cognitive load ...
Part2: On thinking about code (WIP)
- コードを深く (字面だけではなくコード全体の構造) 理解する
- transfer, mental model, 自然言語とプログラミング言語の関係
Part3: On writing better code
- コードを読む観点をPart1, 2 で理解したのでそれを踏まえた上で良いコードを書く方法
- naming, code smells, automatization (無意識で実行できるようにLTMに格納すること)
Part4: On collaborating on code
- 実際のコードを読んだり書いたりというよりもより広いプロセスと cognitive process の話
- Cognitive Dimensions of notation, Cogtnive Dimensions of Code Base, Onboarding, Interruption
# メモ
## On reading code better
- 研究によれば「コードを読む」という行動は一般にプログラマの 60% 近い時間を費やしている
- したがってコードを早く読めるようになるとそれだけ恩恵が大きいことがわかる

- 人が何かの情報を処理しようとする際の脳内構造は、以下の4つのステップがある
- またこれらのステップは、それぞれが独立しているというよりかは並列で稼働しているイメージ
- Filter
- 視覚や聴覚などの情報を処理する部分で、ここではこの情報のうち重要で記憶するべきものは何かという判断をする
- こういった形式で情報を保持することを sensory memory と呼び、特に視覚的な情報を保持することを iconic memory と呼ぶ
- Short-term memory (STM)
- 5 ~ 9個くらいの物事を短時間記憶することができるメモリ
- 例えば、コードを読んでて「この変数はどういう役割を持っている」とか「これは、XXXという処理をしているプログラム」というような情報はこの短期記憶にロードされる
- Working memory
- 何かのプログラムや問題やタスクを解決する際 (コードを読むというのもタスクの1つ) に使うメモリ
- STM と同様に短時間で小容量の情報しか格納できない
- 例えば、コードを読んでる際の今読んでる行が何をするコードかを理解して次の行を読みにいくみたいな行為の時にこれを利用している
- Long-term memory (LTM)
- 長期的にしかも大容量の情報を保持することができるエリアでこれらは、学習を通して蓄積されていく
- 例えば、Java一般の知識やデザインパターンの知識など「知っていること」がここに該当する
例えばコードを読む時には、以下のように働く
1. Ionic memory を使ってどの情報が重要かを判断する
2. Long-term memory から自分が身につけてる知識をロードする
3. Short-term memory を使って読み進めていく
4. Working memory 上で何を実際にしているのかについて深く考える
この際、以下の観点で工夫をすることが可能
### Short-term memory にロードする情報をまとめて (chunk して) ロードできる1つの情報を大きくする
- Adrian de Groot によるチェスプレイヤーの実験である対局の途中の盤面を見せたときに熟練のチェスプレイヤーは平均的なプレイヤーよりも盤面を1から再現する再現率が高いことがわかった
- 熟練なチェスプレイヤーは盤面の駒1つ1つを覚えているのではなく、経験に紐づけて将棋で言えば「穴熊!」みたいな感じで経験に基づいてより多くの情報を1つにまとめることで多くの情報を Short-term memory にロードしている
- このように 5 ~ 9個の情報しかロードできないが、情報の大きさを大きくすることで工夫できる
- プログラミングでは、例えば以下のような方法を用いることで chunking を促進させたりすることができる
- Design Pattern を利用したり
- 抽象的なコードコメントを書いたり
- tree や node などある程度の機能を抽象的に説明する単語を利用する (beaconと呼ぶ)
### Long-term memory により多くの情報を保存して Short-term memory の利用を減らすことでより多くの情報を一時保存できるようにする
- Ebbinghaus の忘却曲線からもわかるように LTM も永遠に情報が格納されるわけではなく定期的な利用がないと忘却してしまう
- 研究により、LTMの記憶容量 (Storage Strength) が減るということはなく取り出す能力 (Retrieval Strength) がどんどん劣化する結果、忘却していくと言われていて、Retrieval Strength を鍛えるには以下の2つの方法がある
- Retrieval Practice
- コードを調べたり勉強したりする際に定期的にこの記憶を呼び出すという練習をしたりする
- Elaboration
- LTM の構造はハードディスクの階層構造とは異なりネットワーク構造になっていると言われており、何かを思い出すときには他の単語などとの関連づけにより思い出す
- この構造を利用して最初から何かを記憶する時には他の何かとわかりやすい関連づけを行うことで取り出す能力が高くなる
### Cognitive Load を小さくして Working Memory の圧迫を防ぐ
- Wroking Memory も STM 同様に限られたサイズと時間しかない。STMとの違いは、情報を格納する場所ではなく情報を実際に利用するメモリであるということ
- John Sweller の [[Cognitive Architecture and Instructional Design]] にあるように Cognitive Load が Working Memory の圧迫に影響する
- Cognitve Load は、
- intrinsic cognitive load
- 問題を解決、タスクの完了に必ず必要になる認知的負荷
- 例えば、「リストを走査して filterをかける」みたいなことはどういうコードを書いたとしても理解する必要がある
- extraneous cognitive load
- intrinsic cognitive load に追加でかかる余計な認知的負荷
- 例えば、意味のない変数名ややたら長いメソッドなど表現上の認知的負荷であることが多い
- これがあると本来必要のない認知的負荷をかけてコードを読む必要がある
- germene cognitive load : ここでは一旦扱わない
- こういった Cognitive Load は Cogniitve Refactroing を通して解消することができる
- 必ずしも保守性の高いコードが読みやすいとは限らないということに基づくリファクタリングで多くの場合は理解するためのリファクタリングになるので一時的だったり、マージしない場合もある (読むために故めんどを書くとか)
- reverse refactoring
- メソッド化の反対でインライン展開することでそこだけを読めば理解できるようにする
- reorder method
- 単純にメソッドの順番を変えたりする
- ただし本当にやりたいことが複雑で extraneous cognitive load を小さくできないこともあるのでそういう時には Memory aids を利用する (Working Memory の利用を補助するもの)
- Dependency Graph を書いて全体構造を可視化する
- e.g. 紙やpdfに印刷して変数の依存とかを書く、クラス図を書くなど
- State Table をつくる
- あるスナップショットでそれぞれの変数などがどういった状態になっているかの表を書く
## On thinking about code
### コードの深掘りかた
- コードを深く理解するためのステップ
1. Find a focal point
- まず注目しているコードを探してスタートポイントを決める
2. Expand knowledge from the focal point
- Focal point からコードを探っていって範囲を広げていく
3. Understand a concept from a set of related entities
- Function内での機能やクラスレベルやモジュールレベルでの機能など
4. Understand concepts across multiple entities
- それぞれの相互の関係を探る
- コードを深く掘っていく際に以下の2つの知識レベルがある
- text knowledge
- 字面のままの意味、how の情報
- plan knowledge
- why and why not
- 例えば、コードそれ自体が何をしているかは読み取れるけど、3や4でコード全体の構造がDIなどによってわかりづらくなってる場合には、plan knowledge がわかりづらくなっている
### 自然言語とコードリーディング
- 自然言語の読解能力とコードの読解能力には関連する箇所がいくつかある
- ある研究では、本を読む時と同様に30%の時間で全体の70%を読んでいることがわかっている
- 自然言語と唯一異なる箇所は、上から下にではなくコールスタックを見て読むことが多い
- 自然言語を読む際のプラクティスのうちコードを読む際に使えるものも紹介する
- Activating : Actively thinking of related things to activate prior knowledge
- 関連する知識を思い浮かべながら読んで、LTMからなるべく情報を引き出すように読む
- Monitoring : Keeping track of your understanding of a text
- 途中で分からなかったことや分かっていることもメモをする
- 結構コードリーディングのためにコメントを書くこと多い <img src='https://scrapbox.io/api/pages/omuomugin/omuomugin/icon' alt='omuomugin.icon' height="19.5"/>
- Determining importance : Deciding what parts of a text are most releavant
- 重要なところとそうでないところを分ける
- Inferring : Filling in facts that are not explicitly given in the text
- 変数名などから読み取れる情報からある程度の context を推察する
- Visualizing : Drawing diagrams of the read text to deepen understanding
- 可視化することで構造全体を見やすくする
- Questioning : Asking questions about the text at hand
- コードに対する疑問を途中ですることで理解を確認していく
- e.g.
- What are the five most centeral concepts of the code? These could be identifier names, themes, classes, or information found in comments.
- What strategies did you use to identify the central concepts? For example, did you look at method names, documentation, or variable names or draw on your prior knowkedge of a system?
- What are the five most central computer science concepts in the code? These could be algorithms, data strcutures, assumptions, or techniques.
- What can you determine about the decisiuons made by the creator(s) of the code - for example, the decision to implement a certain version of an algorithm, use a certain design pattern, or use a certain library or API?
- What assumptions do these decisions rely on?
- What are the benefits of these decisions?
- What are some potential downsides of these decisions?
- Can you think of alternative solutions?
- Summarizing : Creating a short summary of a text
### Mental Model
- 紙などで可視化される model (state table や dependency graph など) の他に脳内で展開するものも model のこと
- e.g.
- folder という構造は実際にはバイナリでの表現にはないけれど経験に基づいて抽象化している
- Tree という構造はメモリにどうマッピングするかにはそこまで注目していなくその抽象化された構造はメンタルモデル
- [[Notional Machine]]
- Mental Model のうち特にプログラムをどう実行するかに特化したもの
- e.g.
- Java の参照渡しの概念などは、メモリ上どうなっているかよりも抽象化されている
- Map, Set, List なども Mental Model になっている
- このようなメンタルモデルは、「走査する」「値を返す」とかの共通の言葉としても利用できる
- 抽象化の仕方によっては混乱を招く場合もある
- e.g.
- 「変数は箱である」「変数は名前やタグづけである」はどちらかしか正しくなく状況によっては混乱を招く
### Learning multiple programming language
- すでに獲得している知識が他の知識の獲得に役立つことを transfer と呼ぶ
- [[transfer during learning]]
- 他の知識を学ぶ時に関連づけをしてLTMに情報を格納していくことで知識間に強い関連をつけていくこと
- 前に紹介した ellaboration が これにあたる
- [[transfer for learning]]
- LTM に格納された情報が知識の獲得を支援すること
- これは、無意識のうちに起こることも意識的に起こることもある
- ズボンを新しく買ったときには、その履き方とかに困ることはなく新しいズボンの履き方を獲得するのは無意識の transfer
- Python を知ってる状態で Javascript で同じようなことをやるにはどうするの?などは意識的な transfer
- transfer の効率性を決めるのは以下
- Mastery
- transfer の元の知識のレベル
- Similarity
- transfer 先がどの程度 transfer 元との関連が強いか
- Context
- IDE や OS や物理的な場所や1つのPCでやってるかなどの環境要因
- Critical attributes
- どの観点が活かせるかを理解できているかを理解しているか (濃淡がついているかなど)
- Association
- どれくらい関連度が高いと思っているか、認識があるか
- Emotions
- transfer 先の獲得にどれくらいモチベーションがあるか
- transfer にもいくつかの種類がある
- High- and Low-road transfer
- [[Low-road transfer]]
- 無意識のうちに transfer できるレベルのもの
- [[High-road transfer]]
- 意識的に transfer できるもの
- Near and Far transfer
- [[Near transfer]]
- transfer 元と先の知識の関連度が強い
- [[Far transfer]]
- transfer 元と先の知識の関連度が薄い
- 一般に far transfer は難しいことがわかっている
- Positive and Negative
- 何かを知ってることで他の理解を助けることを [[positive transfer]] と呼ぶ
- 逆に足枷になってしまうようなことを [[negative transfer]] と呼ぶ
- こちらは混乱やバグにつながる
- 例えば、file を open したら明示的に close しないといけないということが Python で自動でやってくれるのを知っていると忘れることがあるなど
- [[negative transfer]] は [[misconception]] (要するに主観による勘違い) につながることがある
- LTM に格納されている情報が誤っていることに起因するので、正しい情報に更新する必要があるので、「XXX が異なっている」という表面だけの指摘では不十分になる
- これが [[unlearning]] という工程
- 格納されている情報を上書きすることはできないということが研究でわかっていて、正確には、 retriveal strength を修正する必要がある
- "Visual Prgram Simulation in Introductroy Programming Education" の A-1 table に162個のmisconception pattern が載ってるのでそれも要参照
- 最初に何かを学ぶ時人は、 [[negative transfer]] を起こしやすい
- 学ぶ際にいかに open mind になれるか
- よくある misconception を学んでおいて意識する
## On writing better code
### naming
- 良い命名は、LTMから正しく必要な情報を retrieve することができる
- 悪い命名は、misconception を起こしやすい
- なぜそもそも命名が重要か
- Eclipse のコードの 33% のトークン、 72%の文字が命名に利用されている
- コードレビューで触れられる機会が10%程度あり、1/4のコードレビューが命名に関係がある
- 命名はコードベース内に存在するドキュメントとしての機能を持っているので、extraneous cognitive load に大いに影響がある
- Beacon として LTMから正しい関連した情報を取得するのを手助ける
- いい命名とは
- 構造的に似ている (単語の切り方などある程度の構造的な一貫性を守る)
- STM にロードする際に構造に一貫性があると理解しやすい
- NG -> strNameとstringNameとnameStr, countとc など
- 名前に省略を使うか否か
- ある研究によるとある程度長くても単語を変数名に使う方がコードの理解が促進される
- ただし長すぎる場合には、逆に覚えられないということも実験からわかっている
- -> 従って長すぎない範囲で単語を利用することが変数名としては最もコードの理解を促進させる
- コードベースを通して一貫性のある単語を利用する
- NG -> count, length とかで似ている異なった単語を使うこと
- [[name molds]]
- ある2人の開発者が同じ変数名に至るケースは7%程度だった一方でほとんどの場合では、変数名自体を理解することができた
- これは、いろんな name molds (名前の亜種) があるから
- しかし LTM からの retreive を考えると一貫性のある名前を用いる方が良いので以下のようなステップがおすすめ
1. Select the concepts to include in the name
2. CHoose the words to represent each concept
3. Construct a name using these words
- よく見るとこれは、 [[ドメイン駆動設計]] そのものだったりする <img src='https://scrapbox.io/api/pages/omuomugin/omuomugin/icon' alt='omuomugin.icon' height="19.5"/>
- こういった命名による負を [[linguistic antipatterns]] とも呼ぶ
- ある研究によると、最初の命名から命名が変わることはそんなにないことがわかっているので命名にはある程度の時間を使って考えるべき
- 
- Code smell は Cognitive Process の観点で説明ができる
- [[linguistic antipatterns]] と比較して [[structual antipatterns]] と呼ぶ
- Long Parameters List, Complex Switch Statements
- 情報が多いので Working Memory のキャパシティをいっぱいにしてしまう
- Code Clones
- 似たコードは似たものとして認識してしまうせいで、misconception に繋がりやすい
### Cognitive load を測定する
- シンプルで批判も多く発生したがいまだに利用されている PAAS Scale
- 開発者に以下の9段階のうちどれかをつけてもらう
1. Very, very low mental effort
2. Very low mental effort
3. Low mental effort
4. Rather low mental effort
5. Neither high or low mental effort
6. Rather high mental effort
7. High mental effort
8. Very high mental effort
9. Very, very high mental effort
- 他にも eye-tracking や 脳の活性している部分を見るなどが測定方法としては利用されることもある
### Automatization
- 問題解決のためには無意識にできる当たり前を増やしてより高度なことに Working Memory や STM を使えるようにすることが大事
- 極端な例だと走ったり歩いたりなど無意識でもできるようになる学習を Automatization と呼ぶ
- これらの情報はLTMに格納されており、retrive することで活用できる
- 
- Procedural or implicit : 無意識にできるようになっている知識
- Declaritve or explicit : 意識して使う知識
- Episodic : 経験や体験に基づくような知識
- experts はこのメモリ知識をよく使うことが研究でわかっている
- Semantic : 事実など
- ある知識を Automatization な知識にするステップ
1. Cognitive Phase
- 意識的に知識を適用する
2. Associative Phase
- 無意識になるまでパターン学習を繰り返す
3. Aitonomous Phase
- 無意識に行えるようになる
- とにかく量をこなすよりも、うまくいっている例など ([[worked example]]) を参考にする方が学習効率が捗る
- これは、[[worked example]] によってWorking Memory に余裕ができた結果LTMに対して知識のアップデートを行えるからだと推測されている
- プログラミングでは、人のコードや既存のコードを読むことが [[worked example]] にあたる
- こういった LTM に対して学習をインプットしていくことを Germene Cognitive Load と呼ぶ
## On collaborating on code
### Interruption
- 研究により、プログラマが途中で割り込みが入るとコードを書くのを再開するまで 25min ほどかかり、10%程度のプログラマしか1min以内に仕事に戻れない
- 割り込みは、STM と Working Memory が揮発してしまうことにより発生するので以下のような対策をする
- メンタルモデルを図や紙やソースコードコメントなどに起こしておいて STM や Working Memory にロードしているものをダンプする
- TODO をうまく管理する
- チケットをあらかじめ細かく分割しておいてゴールを見失わないようにする
- チケットのサブチケットにしたり、ソースコードにメモしたり
### Cognitive Dimensions of Codebases (CDCB) and Cognitive Dimensions of notations (CDN)
- Cognitive Dimensions of Codebases はフレームワークや言語など一般的にプログラマが開発するものではなく適用するものに対して様々な観点で評価をして適切にバランスを取れるようにするための観点表 (当然必ず全てが良くないといけないわけじゃない)
- Error Proneness (エラーの発生のしやすさ)
- 例えば、静的型付け言語を使うとエラーが発生しにくくなる
- Consistency (一貫性)
- 似たようなものは似たようにかかれているかなど
- Structural な観点
- Diffuseness
- どれくらい簡潔に少ない文字数で書くことができるか
- Hidden Dependencies
- HTMLに対しての js ファイルとか別のファイルに書かれたクラスなどの依存関係
- Provisionality
- 例えばプレビュー機能などどれだけすぐに変更を確認できるかの度合い
- Viscosity
- 変更する際の作業量 (例えば静的型付けは型を書かないといけないので大きいなど)
- Progressive Evaluation
- 中途半端でも実行可能かどうか
- Closeness of mapping
- 言語やライブラリの用途がどれだけ限定されているか
- Hard Menral Operations
- ポインタを使う、関数型言語の知識が必要などそういった知識や Notional Machine の部分
- Secondary Notation
- named parameter に代表されるようなソースコードに対して読み手に付加情報を与えることができる機能、要素
- Abstractions
- サブクラスを適切に作成できるかなど
- Visibility
- システム間やモジュール間の関連がどれだけ見通しやすいか
- これらは Cognitive Dimensions of notations との関連を考えることもできる
- CDN とは、以下の5つの作業を表現している。これらの活動はソフトウェアのライフサイクルによってもどういったアクションに力を入れるか変わってくる
- Searching
- 該当箇所を探す
- Comprehension
- 実行したいことを実際のコードとして計画、設計する
- Transcription
- 実際にコードを書く
- Incrementation
- 新しく機能を追加する
- Exploration
- コードベースを読んで課題を見つける
- 以下のようにある CDCB は適用すると CDN の何かを改善するという関係にある。ただし、トレードオフになっている項目もあるためライフサイクルに応じてどの活動を優先するかというバランスを格ソフトウェアで考える必要がある
- 
- 
### Onboarding
- 多くの場合 newbie は LTM がそのチームの他のメンバーよりも情報が格納されていない
- 新人の場合には言語についての情報も少ない
- 新人ではない場合でもドメインについての情報はない
- Piaget's Stages という子供向けの認知開発のフレームワークがあり、プログラマにもこれを当てはめてみる
- Sensorimotor Stage
- 特に背景はなくただ実行してみるだけ
- プ : 実行が行われる仕組みがわからず変数のトレースもできない
- Properrational Stage
- ある程度誤っていても仮説を立てて行動し始める
- プ : トレースはできる、トレースのみからコードの説明を行う
- Concrete Operational Stage
- 特定のシチュエーションに特化して仮説を立てて行動できる、認識を説明できる
- プ : ある特定のコードに対しては、トレースを (実行) しなくても説明できる
- Formal Operational Stage
- 他のシチュエーションなども意識しながら仮説を立てて行動できる、認識を説明できる
- プ : 抽象的なレベルでコードの説明ができる
- プログラマに当てはめた場合の図
- 
### Semantic Wave
- 何かを学ぶ際の理想的な曲線
- 
- アンチパターン
- 初期に抽象的すぎることを教えて unpacking を妨げる
- 中期に具体的すぎることだけを教えて repacking を妨げる
- 後期に repacking を支援しない
### Onboarding と newbie の Cognitive Process の支援
- Support LTM
- 最初に LTM に格納するようなドメインなどの知識をインプットすることで、コードを書いているときなどに LTM から retrive が発生しやすいようにする
- Support STM
- Cognitive Dimensions Notation の Activity のうち1つだけを同時に実行してもらう
- そうすると Working Memory に余裕が出るので LTM への学習ができる
- Support Working Memory
- 図など Working Memory に格納されるようなものを外だししておく
# 感想
- [[Cognitive Architecture and Instructional Design]] に近いことがより詳細に書いてある (この論文も引用されている)
- 若干アカデミックと実際のソフトウェアエンジニアで感覚の違いを感じるような方法はあったがコンセプトそのものは違和感がない上に多くの認知科学の研究を引用してあって数値で語れなかったものなどがより具体的に言語化された
- 特にオンボーディングなどにも coginitive processing を応用していて新鮮だった
- LTM、STM、Working Memory のモデルが終始一貫してたので本全体を通してわかりやすかった