The Programmer's Brain
https://gyazo.com/8fc95ce2524b6f75d92489a0545359c7
なぜこの本を読んだか
著者がプログラミングの理解の研究をしている人で、目次を読んでみると面白そうだったしページ数も250ちょいだったのでサクッと読めそうに思ったのでとりあえず読んでみた
何が書かれている本か
プログラマがコードを読むときにどういう脳内構造になっているかを心理学や認知科学の観点から説明した本
本の内容は以下の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% 近い時間を費やしている
したがってコードを早く読めるようになるとそれだけ恩恵が大きいことがわかる
https://gyazo.com/c238e1d757d28ddb68f545e80b4341c8
人が何かの情報を処理しようとする際の脳内構造は、以下の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との違いは、情報を格納する場所ではなく情報を実際に利用するメモリであるということ
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
途中で分からなかったことや分かっていることもメモをする
結構コードリーディングのためにコメントを書くこと多い omuomugin.icon
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 という構造はメモリにどうマッピングするかにはそこまで注目していなくその抽象化された構造はメンタルモデル
Mental Model のうち特にプログラムをどう実行するかに特化したもの
e.g.
Java の参照渡しの概念などは、メモリ上どうなっているかよりも抽象化されている
Map, Set, List なども Mental Model になっている
このようなメンタルモデルは、「走査する」「値を返す」とかの共通の言葉としても利用できる
抽象化の仕方によっては混乱を招く場合もある
e.g.
「変数は箱である」「変数は名前やタグづけである」はどちらかしか正しくなく状況によっては混乱を招く
Learning multiple programming language
すでに獲得している知識が他の知識の獲得に役立つことを transfer と呼ぶ
他の知識を学ぶ時に関連づけをしてLTMに情報を格納していくことで知識間に強い関連をつけていくこと
前に紹介した ellaboration が これにあたる
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
無意識のうちに transfer できるレベルのもの
意識的に transfer できるもの
Near and Far transfer
transfer 元と先の知識の関連度が強い
transfer 元と先の知識の関連度が薄い
一般に far transfer は難しいことがわかっている
Positive and Negative
こちらは混乱やバグにつながる
例えば、file を open したら明示的に close しないといけないということが Python で自動でやってくれるのを知っていると忘れることがあるなど
LTM に格納されている情報が誤っていることに起因するので、正しい情報に更新する必要があるので、「XXX が異なっている」という表面だけの指摘では不十分になる
格納されている情報を上書きすることはできないということが研究でわかっていて、正確には、 retriveal strength を修正する必要がある
"Visual Prgram Simulation in Introductroy Programming Education" の A-1 table に162個のmisconception pattern が載ってるのでそれも要参照
学ぶ際にいかに 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 とかで似ている異なった単語を使うこと
ある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
よく見るとこれは、 ドメイン駆動設計 そのものだったりする omuomugin.icon ある研究によると、最初の命名から命名が変わることはそんなにないことがわかっているので命名にはある程度の時間を使って考えるべき
https://gyazo.com/8b4f8557d7d211b03e151d7a0b7f0d81
Code smell は Cognitive Process の観点で説明ができる
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 することで活用できる
https://gyazo.com/3b50c846a6a7aa2a31c5d4ee270c1a00
Procedural or implicit : 無意識にできるようになっている知識
Declaritve or explicit : 意識して使う知識
Episodic : 経験や体験に基づくような知識
experts はこのメモリ知識をよく使うことが研究でわかっている
Semantic : 事実など
ある知識を Automatization な知識にするステップ
1. Cognitive Phase
意識的に知識を適用する
2. Associative Phase
無意識になるまでパターン学習を繰り返す
3. Aitonomous Phase
無意識に行えるようになる
これは、worked example によってWorking Memory に余裕ができた結果LTMに対して知識のアップデートを行えるからだと推測されている こういった 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 の何かを改善するという関係にある。ただし、トレードオフになっている項目もあるためライフサイクルに応じてどの活動を優先するかというバランスを格ソフトウェアで考える必要がある
https://gyazo.com/54df2af4194ac1258c53f32fed1c9678
https://gyazo.com/be5a1c5a2fd3e447e2e9a48b02ece91d
Onboarding
多くの場合 newbie は LTM がそのチームの他のメンバーよりも情報が格納されていない
新人の場合には言語についての情報も少ない
新人ではない場合でもドメインについての情報はない
Piaget's Stages という子供向けの認知開発のフレームワークがあり、プログラマにもこれを当てはめてみる
Sensorimotor Stage
特に背景はなくただ実行してみるだけ
プ : 実行が行われる仕組みがわからず変数のトレースもできない
Properrational Stage
ある程度誤っていても仮説を立てて行動し始める
プ : トレースはできる、トレースのみからコードの説明を行う
Concrete Operational Stage
特定のシチュエーションに特化して仮説を立てて行動できる、認識を説明できる
プ : ある特定のコードに対しては、トレースを (実行) しなくても説明できる
Formal Operational Stage
他のシチュエーションなども意識しながら仮説を立てて行動できる、認識を説明できる
プ : 抽象的なレベルでコードの説明ができる
プログラマに当てはめた場合の図
https://gyazo.com/14c534f34f1a76ac68ae0bc50a726b4f
Semantic Wave
何かを学ぶ際の理想的な曲線
https://gyazo.com/bd48b7f3d76262456a81ad70bacbf6cc
アンチパターン
初期に抽象的すぎることを教えて 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 に格納されるようなものを外だししておく
感想
若干アカデミックと実際のソフトウェアエンジニアで感覚の違いを感じるような方法はあったがコンセプトそのものは違和感がない上に多くの認知科学の研究を引用してあって数値で語れなかったものなどがより具体的に言語化された
特にオンボーディングなどにも coginitive processing を応用していて新鮮だった
LTM、STM、Working Memory のモデルが終始一貫してたので本全体を通してわかりやすかった