🧠

『プログラマー脳』を読んで

2023/09/19に公開

どういう本?

  • "認知科学に基づくアプローチ"とタイトルに有る通り、プログラミングに纏わる認知をエビデンスとともに解説してくれる1冊
  • 前半は人間がプログラミングをどのように学習するかについての解説、後半はエビデンスに基づいたプログラミングのテクニックの解説という印象
    • 特に8章は変数命名について解説してくれる章で、すべてのプログラマーにとってためになる話
    • 13章も非常にためになった。新人オンボーディングを、ベテランと新人の認知のズレから解説していくような章になっており、個人的にタイムリーな話題だった。

まとめ

  • 第1章
    • コードを読むときに起こる3種類の認知プロセス
      • 長期記憶: 覚えている構文、キーワードの意味。ROM
      • 短期記憶: 読んでいるときに出てきた変数名、データ構造。RAM
      • ワーキングメモリ: 一度に多くの情報を処理しなければならない負荷。プロセッサ
  • 第2章
    • コードを書くことが重視されがちだが、コードを読むことも重要
      • コードを理解する時間が60%
    • コードの記憶は短期記憶の記憶時間(約30秒)と容量が制約となる
      • 短期記憶には2から6の物事しか記憶できない
      • ただし、チャンクで記憶することでより大きな記憶容量となる
        • チェス盤面を記憶する実験では、チェスプレイヤーは序盤の定石を記憶しているため、より高い正答率となる(ランダムな配置の場合、正答率は一般人並になる)
      • コードもチャンク化することで、短期記憶に効率よく記憶され、より理解しやすくなる
      • チャンク化しやすいコード
        • デザインパターン
        • コメントを書く
        • ビーコンを残す
          • 理解を助ける目印となる行
          • 単純なビーコン(意味のある変数名)
          • 複合的ビーコン(複数行をまとめて読んだときに意味が理解できる箇所)
          • ビーコンはチャンクよりも小さい概念
  • 第3章
    • 文法を覚えることは重要
      • 検索は割り込み。作業を止める
      • フラッシュカードを使って覚える
    • (覚えるだけでなく)忘却を防ぐ
      • 定期的に繰り返すことで定着する
    • 貯蔵強度と検索強度
      • 貯蔵強度:特定の何かが長期記憶にどれだけきちんと保持されているか。繰り返し学習することで強化される
      • 検索強度:特定の何かを思い出すのがいかに容易か。繰り返し思い出すことで強化される
      • 年齢とともに貯蔵強度は増加するが、検索強度は低下する
      • 積極的に覚えようとすることで、記憶が強化される
    • スキーマ
      • 思考とその関係が頭の中で整理されたもの
      • 学習するとき脳内では、既存のスキーマに当てはめようとする
    • 精緻化
      • 覚えたいことを考え、それを既存の記憶と関連付け、新しい記憶を長期記憶にすでに保存されているスキーマに適合させること
      • 新しい情報を覚えるときは、明示的に精緻化していくことが重要
  • 第4章
    • 複雑なコードを理解する
      • ワーキングメモリは2~6の事柄を処理する能力しかない(認知的負荷)
      • 認知的負荷の種類
        • 課題内在性負荷:問題自体の複雑さ
        • 課題外在性負荷:問題の妨げとなる外部要因
        • 学習関連負荷:長期記憶に保持する際に引き起こされる認知的負荷
    • 認知的負荷を軽減するテクニック
      • リファクタリング
        • 認知的リファクタリング
          • 保守性ではなく可読性を高めるリファクタリング
  • 第5章
    • 「見慣れないコードを読むときは認知的負荷が高い」
    • 変数の役割
      • 変数は11の役割に分類できる
        • 固定値
          • 定数
        • ステッパー
          • ループでカウントする変数
        • フラグ
        • ウォーカー
        • 直近の値の保持者
        • 最も重要な値の保持者
        • 収集者
          • データを纏める
        • コンテナ
          • リスト
        • フォロワー
          • これから変化する値を保持しておく
        • オーガナイザー
          • 並べ替え、異なる形式への変換
        • テンポラリ
          • 一時変数、tmp
      • 役割を理解することで、ソースコードの理解がはかどる
    • ハンガリアン記法の見直し
      • 現在のハンガリアン(型接頭辞)は変化した用法
      • 元は、もっと具体的な意味を表していた
        • インスタンスXをcountする変数ならcX
    • プログラムの理解の4ステップ
      • フォーカルポイントを見つける
        • フォーカルポイント:コード内で注目すべき場所
      • フォーカルポイントから知識を拡張する
      • 関連要素から、コードに利用されている概念を理解する
      • 複数の要素を横断して、利用されチエル概念を理解する
    • みんなコードを読むことよりコードを書くことに腐心している
      • 平均的なプログラマーはコードを読むことに勤務時間の60%を使う
      • 計算能力よりも言語能力がプログラミング能力と相関する
      • 文章を理解するようにコードを理解している
      • コードを読む際、全体をスキャンし、コールスタックをたどる
    • 文章理解のための戦略
      • 過去の知識の活性化
      • 監視:分からない部分にコメントを付ける
      • 重要性の判断
      • 可視化
      • 自問自答
      • 要約
  • 第6章
    • プログラミング上の問題に対する推論
    • メンタルモデル
      • 「眼の前の問題に対して推論するためにワーキングメモリの中で概念を抽象化するもの」
      • より良いプログラミングのためには、コードの抽象的なモデルを持つこと
      • 長期記憶にメもンタルモデルが保存される
    • 想定マシン
      • コンピュータが何をしているかを抽象的に考える
      • 変数を「格納する」、変数を「返す」
  • 第7章
    • 2つ目の言語を学ぶのはなぜ簡単か
      • 「転移」
        • すでに知っている情報が役立つ
        • 「学習中の転移」
          • 脳が関連情報を見つける
        • 「学習の転移」
          • すでに知っていることが適用できるとき
        • 「一般道の転移」と「高速道路の転移」
          • 意識せずに実行できるか、意識して獲得するか
        • 「近転移」と「遠転移」
          • 領域間の距離
          • JavaからC#は近いが、JavaからPrologは遠い
        • 「正の転移」と「負の転移」
          • 転移は良い影響を与える場合と悪い影響を与える場合がある
          • 言語同士で異なる部分が間違いを生む
          • オブジェクト指向言語から関数型言語への転移など
      • 「誤認識」
        • 本来は関係ないが類似点のある事柄同士で転移してしまったゆえの勘違い
        • プログラミングにおいても起こりやすい
      • 「概念変化」でデバッグする
        • 新しく言語を学ぶ際、既存言語からその言語に適したメンタルモデルに置き換える
        • アンラーニングが必要
      • 誤認識を防ぐために
        • 間違っている可能性があることを認識する
        • 思い込みを認識する
        • 先輩にアドバイスを貰う
        • ペアプログラミング
        • テスト
        • ドキュメント
  • 第8章
    • 命名の重要性
      • コードの大部分を占める
      • コードレビューの1/4は命名に関する指摘
      • 最もアクセスしやすいドキュメントと言える
      • ビーコンとして機能する
    • 命名方法
      • バトラーの命名規則
        • 大文字は適切に
        • _を連続使用しない
        • 意味のわかる単語のみを使用し、略称は一般的な場合のみ
        • 単語数は2~4, 5以上にはしない
        • 8文字未満にしない、ただし一部の1文字命名は許可
        • 列挙型はアルファベット順に
        • 先頭にアンダースコアは使用しない
        • ハンガリアン記法を使用しない
        • 長い識別子名は避ける
        • 変に大文字と小文字を組み合わせない
        • 数字を意味する単語だけで構成しない
      • 一貫性が重要
      • 最初の命名が後に響く
    • 短期記憶の働きを助ける命名形式
      • 一貫性
    • なぜ命名は大変か
      • コーディング中は認知的負荷が高い状態だから
      • コードレビューなどで振り返ること
    • 単語を略すべきか
      • 省略しないほうがバグをより多く見つけられるという研究
      • 一方で覚えづらくなるため、バランスが必要
    • 1文字の変数
      • i,j,k,n以外の変数名は意味のコンセンサスがない
    • 名前の雛形
      • 変数名を決める際に利用される典型的なパターンのこと
      • max_benefit_amountがすでにあるなら、mav_interest_amountは簡単に関連付けられる
    • 命名の3ステップモデル
      • 名前に含めるべき概念を選択する
      • それらの各概念を表す単語を選ぶ
      • それらの単語を使って命名を行う
  • 第9章
    • 「コードスメル」(コードの臭い)
      • コードが読みづらくなる原因
        • 長いメソッド、神クラス(怠け者クラス)、複雑なSwitch文, 重複したコード, etc.
      • 長すぎるとワーキングメモリに収まらない
      • 効率的にチャンク化できない
      • 誤ったチャンク化を誘発する
    • 「言語的アンチパターン」
      • ex. is始まりの変数がboolでない
      • 大きな認知的負荷を誘発する
  • 第10章
    • 問題解決とは
      • 問題解決を構成する要素
        • ゴール状態
          • 達成したいものに到達したとき
        • スタート状態
          • 問題を解決するに当たってのスタート
        • ルール
        • スタート状態からゴール状態に到達するための規定
      • 状態空間
        • プログラムを解くときに考えうるすべてのステップ
    • 問題解決を解決する際の長期記憶の役割
      • ポリアの「思考のシステム」
        • 問題を理解する
        • 計画を立てる(プログラミング言語への翻訳)
        • 計画を実行する(解決)
      • 見慣れた問題なら脳はより簡単に解決できる
      • ワーキングメモリが、短期記憶から要件や検索した既存コードを取り出し、超記憶から関連する背景知識を検索する
        • 問題解決には、長期記憶の検索プロセスが重要
      • 「手続き記憶」と「宣言的記憶」
        • 意識せずできること(運動など)とできないこと(大統領の名前など)
        • 宣言的記憶は「エピソード記憶」と「意味記憶」に分けられる
          • 問題を解くにはエピソード記憶が重要
            • 過去に解決した問題など
    • 自動化
      • スキルは練習すると何も考えずにできるようになる
        • スキルツリーのように、一つ自動化すればより複雑なスキルに到達できる
        • 自動化すれば認知的負荷が生じない
      • 自動化のための段階
        • 認知的段階:考える必要がある
        • 連合的段階:効率的な記憶
        • 自律的段階:直感的
      • 自動化するとプログラミングが早くなる
        • 「インスタンス理論」
          • すべての記憶は、より抽象的なクラスのインスタンスである
          • 以前のタスクと同じような場面に遭ったとき、エピソード記憶から思い出す
          • このとき、タスクの実行がエピソード記憶の情報のみで完結し、推論を行わなくなったとき、自動化される。
      • 潜在記憶の改善
        • 楽器の運指練習でトーンラダーをするように、プログラミングでも意図的に練習する
          • ex. for文を形を変えて書いてみる
    • 範例
      • 他人の解放を研究すること
      • 問題解決のために効果的
      • 「学習関連負荷」
        • 情報を長期記憶に保存する負荷
        • 認知的負荷がかかりすぎてこの負荷にメモリを割けないと意味がない
        • 範例は負荷を軽減する
      • 範例の活用
        • ペアプロ
        • GitHub探検
        • 本やブログ記事を読む
  • 第11章
    • CDN(cognitive dimensional of notation)フレームワーク
      • プログラミング中の活動のマトリクス
      • プログラミング中に行う活動は「検索」「理解」「転写」「増強」「探索」に分けられる
    • 検索
      • コード内を調べ特定の情報を探す
    • 理解
      • コードを読んで機能を把握する
    • 転写
      • コードを書く
    • 増強
      • 検索+理解+転写
    • 探索
      • とりあえず探索的に書いてみる
    • 割り込み中断
      • 1度の中断で15〜20分奪われる
      • 特に最近はSlackなどに割り込まれる
    • ウォーミングアップ
      • プログラミング中には難しい瞬間と簡単な瞬間がある
      • コードのメンタルモデルを構築し、転写活動を開始するための準備時間は「ゾーンに入る」ための時間
    • ロードブロックリマインダ
      • 割り込み作業に入る前に、適当な文字を挿入して目印にする
    • 割り込みを受けたらやるべきこと
      • メンタルモデルを保存する
        • コメントやドキュメントにメモを残しておく(ブレインダンプ)
      • 展望記憶を補助する
        • 宣言的記憶と手続き記憶以外に展望記憶がある
        • 展望記憶は未来に関係する記憶
          • 牛乳を買い忘れないように記憶にとどめておく
        • TODOコメントをつける
      • 会目標のラベル付け
      • タスクを分割してコメントに書いておく
    • マルチタスク
      • 自律的段階に達していない2つ以上のタスクは同時にこなせない
  • 第12章
    • CDCB(Cognitive Dimensions of code bases; コードベースの認知特性)
      • CDNを言語やコードに拡張する
      • 「エラー発生のしやすさ」
        • JSやPythonは発生しやすい
      • 「一貫性」
        • 命名に一貫性がないと認知的負荷が高い
      • 「拡散性」
        • 他の言語よりも多く書かないと同じ機能が実現できない場合のように、どれだけ多くの記述が必要か
      • 「隠れた依存関係」
        • ドキュメントを書くことでわかりやすくできる
      • 「暫定性」
        • 走りながら考えることが容易か
      • 「粘性」
        • 現在のシステムのコードの変更難易度
      • 「段階的評価」
        • 書きかけでチェック・実行することの難易度
      • 「役割表現力」
        • 関数を表現する()、シンタックスハイライトなど、わかりやすくする要素
      • 「マッピングの近接度」
        • APLは一見わかりにくいが、ベクトル計算領域とマッピング度が高い
      • 「ハードな心的操作」
        • 多くのことを記憶していないと使えない機能はハードな心的操作を求める
      • 「副次的表記」
        • コメント、命名引数
      • 「抽象化」
        • 関数、オブジェクト、クラスなどの共通した言語仕様
      • 「視認性」
        • 単なる文字列は視認性が低い
  • 第13章
    • オンボーディングのために
      • シニア開発者は新人に一度に多くの情報を与えて質問や小さいタスクを投げがち
      • 新人はチャンク不足やスキル自動化の不足で認知的負荷が高くなってうまくオンボーディングできながち
    • 「専門知識の呪い」
      • 自分が覚えた過程を忘れて、過大評価してしまう
    • 熟練者と初心者は覚える過程が違う
      • 初心者の行動を理解する
      • 「ピアジェのオリジナルモデル」
        • 幼児期からの行動段階
        • プログラマーに対応させてみる(新ピアジェ主義)
          • 「感覚運動期」
            • 2歳まで
            • 計画や戦略は立てることができず、感覚に基づいて動く
            • プログラマー:プログラムの実行について、全く正しい理解しか持たない
          • 「前操作期」
            • 2~7歳
            • 仮説や計画を立て始めるが、それを思考に活かすことはできない
            • プログラマー:一部のコードを手作業で予測
          • 「具体的操作期」
            • 7~11歳
            • 目に見える具体的なものについては仮説を立てられるが、一般的な結論を出せない
            • プログラマー」機能的なアプローチではなく、コードを読んで演繹的に理由付けできる
          • 「形式的操作期」
            • 11歳以降
            • 形式的な推論ができる
            • 論理的で一貫性のある体系的な推論ができる
        • あたらしい情報を学ぶと一時的に物事を忘れてしまうことがある
          • 一時的に低い段階に移行することもある
        • 「意味波」
          • 抽象→具体→抽象のように波を描いて理解する
    • オンボーディングを改善するための活動
      • タスクにおけるプログラミングに関する活動を1つに限定する
        • 転写、探索、理解、検索、増強から絞る
    • 新人の記憶をサポートする
      • 長期記憶のサポートのために、関連情報を説明する
        • 「Laravelを使っていて、Jenkinsを使ってHerokuにデプロイします」は理解できない可能性がある
      • 短期記憶をサポートする
        • 小さくて一つのことだけに特化したタスクを用意する
          • 複数の活動を一度に行うと短期記憶に負荷がかかる
      • コードを一緒に読む
        • 活性化
          • コードリーディング前に、コードにどのような概念が登場するかを予め確認する
        • 重要性の判断
          • どこが重要であるかを伝える
        • 推論
          • 業務知識を共有する
        • 監視
          • 新人自身がどの理解度にいるのかを把握する事が重要
          • 復習をするように伝える
        • 可視化
          • 図で伝える、図を書かせる
        • 自問自答
          • 質問やりとりの際は新人の認知的負荷を監視する
            • 憶測で答えていたり、意味のない結論を出し始めたら、認知的負荷が高い状態
        • 要約
          • 一緒に読んだコードの内容を要約としてまとめる

Discussion