👻

認知負荷について -少し解像度を上げてみた-

2023/09/29に公開

本記事のゴール

認知負荷という言葉をエンジニアとして正確に理解したいと思いました。ただゴールが理解だと漠然としてるので、最終的には認知負荷とは何でどの様に付き合えばいいのかを記載できればと思っております。

初めに

こんなことないですか?

開発の時の話です。依頼者からの開発内容を把握して、既存のコードへの影響調査をして、コードを開発することをやっていきますが…そもそも環境構築をしないといけない…しかも複雑な手順で…やっと環境構築ができたと思ったら、アプリがエラーを吐きまくる…調査…開発できるようになったら、コードはグローバル変数を多用してたり、条件分岐が多い if のネストが 5 段階…コードで SQL を作成してるが、条件分岐が多く…SQL の全容わからない。

このような時、このアプリってクソだな…コードもクソだな…と思うかもしれないです。
ただ、クソだなで片付けてしまうと、今後の改善などには繋がらなくなってしまうので、
一体何がクソなのかもう少し、クソと思われる部分の解像度を上げたいと思っていました。

出会い

最近業務で何やってるかわからないものを紐解き、移行作業をする経験をしました。この意向を通じて、ビジネス側で利用されている部分がブラックボックス化してたのを dbt に書き換え、全容を把握しフローを整理できました。ただ、チームには漠然と大変なことをお願いしてしまったなと思っていましたが、負担の部分を言語化する明確な言語を持ち合わせていませんでした。

そんな中、ある日下記のポッドキャストの回に偶然出会いました。

そこでは、t_wada さんがいわゆるクソコードの解像度を上げていくために認知負荷を用いてわかりやすく説明してくれていました。私自身のモヤモヤが晴れた感覚がありました。認知負荷をもう少し自らの言葉で説明できるくらいまで落とし込めば、言語化できそうだと。

ここまでが序論です。
ここから、認知負荷についての説明になっていきます。

認知負荷とは

簡単に書くと、下記になると思っております。

認知負荷(Congnitive Load)とは、私たちのワーキングメモリにかかる負荷のこと
出典:Cognitive Load Theory A Guide to Applying Cognitive Load Theory to Your Teaching

色々と謎な用語が出てきたので、少し解説していきます。
認知負荷を理解するには、認知的負荷理論なるものが存在します。
Information Processing Model

出典
Adapted from Atkinson, R.C. and Shiffrin, R.M. (1968). 'Human memory: A Proposed
System and its Control Processes'. In Spence, K.W. and Spence, J.T. The psychology of
learning and motivation, (Volume 2). New York: Academic Press. pp. 89–195.

認知的負荷理論は、上図のような人間の情報処理モデルに基づいています。
このモデルでは、記憶を 3 つの主要な部分があると説明しています。

  • 感覚記憶
    • 私たちの周りで起こっていることのほとんどをフィルタリングし、選択した情報をワーキングメモリに渡して追加処理させる
  • 作業記憶(ワーキングメモリ)
    • 通常、5~9 個の情報の断片(チャンク)を処理することができる
    • ワーキングメモリは、その情報を破棄するか、長期記憶に保存するために分類する。
  • 長期記憶
    • 「スキーマ(構造化した知識)」と呼ばれる構造で情報を保存し、スキーマは情報の使い方に基づいて情報を整理する
    • これらのスキーマは、使えば使うほど発達し、思い出しやすくなる

認知負荷を詳細に書くと 学習者が長期記憶保持とスキーマに効率的に処理できる以上の負荷を学習者へ与えた時に起きる事象のこと を指すと言えるでしょう。

  • 認知負荷は全てが良くないことなのでしょうか?
  • 認知負荷を起こさないようにすればいいのでしょうか?

個人的には NO となります。

認知負荷の分類

NO と考える理由の前に、まず認知負荷をもう少し分解してみましょう。
認知負荷は下記のような加算法で表すことができます。

Intrinsic Load+Extraneous Load+Germane Load=総認知負荷

あらためて、認知負荷を説明すると、

認知負荷とは人のそれぞれのワーキングメモリの許容量を総認知負荷が超えると発生ます。
(Gerjets, Scheiter and Cierniak 2009)。

ここからは、加算法のそれぞれの項目についてみていきたいと思います。

Intrinsic Load(課題内在性負荷)

学習課題そのものが持っている認知負荷のこと。
内容が複雑であればあるほど当然負荷は高くなる。

Extraneous Load(課題外在性負荷)

学習課題とは直接関係のない認知負荷のこと。
ここの負荷を軽減することが大切。

    • 複雑な指示や見にくいワークシート
    • 刺激の多い学習環境
    • 「失敗したらどうしよう」といった学習とは関係ない学習者自身の余計な考え

Germane Load(学習関連負荷)

知識の構築に役立つ認知負荷のこと。
ここの負荷を増やしていくことが大切。

    • 「異なるタイプの問題を混ぜて交互に解く」場合、「同じタイプの問題のみを繰り返し解く」場合よりも認知負荷は高くなるが、同時に学習内容の定着率も高くなる
    • Javaだと関数言語ライクのコードの書き方のメリットが理解できなかった。ただ JavaScript で関数言語ライクな書き方を学び、関数言語の概要は把握できた。その結果 Java の関数ライクな書き方も意図がなんとなくわかり、拒絶反応なく書くことができる様になった

つまり(NOの理由)

認知負荷の分類項目は、下記のような説明をできると思います。

本来の課題(Intrinsic Load)に立ち向かうために、外部要因(Extraneous Load)を軽減させながら、学習(Germane Load)を続けながら、本来の課題(Intrinsic Load)に集中することが必要だといえる。

  • Intrinsic Load(課題内在性負荷)は減らせない負荷
  • Extraneous Load(課題外在性負荷)は減らしたほうがいい負荷
  • Germane Load(学習関連負荷)は知識向上のために必要な負荷

となるので 認知負荷 = すべて悪い というわけではないはずです。

初めにの例に戻ってみましょう。

ここからは解像度を上げるために、初めにの例に対し認知負荷の分類に当てはめてみましょう。

開発する場合に、依頼者からの開発内容を把握して、既存のコードへの影響調査をして、コードを開発することをやっていきますが…そもそも環境構築をしないといけない…しかも複雑な手順で…やっと環境構築ができたと思ったら、アプリがエラーを吐きまくる…調査…開発できるようになったら、コードはグローバル変数を多用してたり、条件分岐が多いifのネストが5段階…コードでSQLを作成してるが、条件分岐が多く…SQLの全容わからない。

この文章を認知負荷の 3 タイプに分類してみましょう。
分類をする前に個人(企業に所属でチーム開発をしてる)としての責務を明確にしておきます。
*ここでの分類はあくまでも個人の判断でやってます。


- Intrinsic Load(課題内在性負荷)
    - 依頼者からの開発内容を把握
    - 既存のコードへの影響調査
    - コードを開発する
- Extraneous Load(課題外在性負荷)
    - 環境構築(複雑な手順)    
    - アプリがエラー(調査…)
    - コードはグローバル変数を多用してたり、条件分岐が多いifのネストが5段階…
    - 条件分岐が多く…SQLの全容わからない。
- Germane Load(学習関連負荷)
    - なし

Intrinsic Load(課題内在性負荷)と同じくらい Extraneous Load(課題外在性負荷) が存在してるのがわかります。つまり、個人の責務を果たすために、責務と直接関係ない部分でやらないといけないことがあるみたいです。そうなると、ワーキングメモリを利用してしまって、本来の課題を解く時にはワーキングメモリの容量がなくなってしまうことになると考えられます。

よって、本来の作業をする前に他のことに注意が取られるため、結果としてこの環境はクソだと考える様になるのではないでしょうか? 少なくとも私自身はそう思ってしまうと思います。

認知負荷(Congnitive Load)に対して何をする必要があるのか?

認知負荷(Congnitive Load)と付き合っていくためには、認知負荷(Congnitive Load)それぞれのタイプにおいて、何をしないといけないのか、またどの様な心持ちで対応していけばいいのかを把握することが個人的には重要だと思っています。

「快適な」学習のために〜認知負荷理論入門を参考に何をしていけばいいかを記載してみます。


- 学習課題に関すること
    - 情報源の統合
    - 例題の活用
    - 熟達度に応じた課題設定
- 学習者自身に関すること
    - 協働(Collaboration))
    - ジェスチャー(Gesturing)
    - 動機付け(Motivational Cues)
- 学習環境に関すること
    - 刺激物の除去(Attention-capturing-stimuli reduction)
    - 目を閉じる(Eye Closure)
    - ストレス低減のためのアクティビティ導入(Stress-Suppressing Activities)

少し独自に 3 タイプに分類してみました。


- Intrinsic Load(課題内在性負荷)の対策
    - 学習課題に関すること
        - 情報源の統合
        - 例題の活用
        - 熟達度に応じた課題設定
- Extraneous Load(課題外在性負荷)の対策
    - 学習環境に関すること
        - 刺激物の除去(Attention-capturing-stimuli reduction)
        - 目を閉じる(Eye Closure)
        - ストレス低減のためのアクティビティ導入(Stress-Suppressing Activities)
- Germane Load(学習関連負荷)
    - 学習者自身に関すること
        - 協働(Collaboration))
        - ジェスチャー(Gesturing)
        - 動機付け(Motivational Cues)

ただ、こちらのまとめはあくまでも教育領域での対応となります。
ここの分類をベースに私自身エンジニアのため、エンジニア視点で再分類をしてみます。
*あくまでも個人の判断の分類になります。


- Intrinsic Load(課題内在性負荷)の対策
    - 原則:負荷を減らすことはできないが、わかりやすい(管理しやすい)状態にする
        - 課題を整理しましょう
        - 課題からタスクを作成する場合に小さく小分けにして対応しましょう
- Extraneous Load(課題外在性負荷)の対策
    - 原則:Extraneous Load(課題外在性負荷)を最小限に減らす。
        - 減らし方の例
            - 事前にエラーの処理について、取り決めがなく、エラー処理の実装で悩む場合、
             依頼者(PO)と非機能の要件を決めましょう。
            - コードの書き方が人によって異なり、可読性が悪い場合、linterを導入しましょう
            - ディレクトリの取り扱いが人によって異なる場合、
             チームでディレクトリの取り扱いを決めましょう
- Germane Load(学習関連負荷)の対策
    - 原則:長期記憶の中に取り込む重要負荷であり、最大化していきたい
        - 新技術の調査検証
        - 他社事例などの把握
        - チーム内、有識者との会話

このように分類できるのではないでしょうか?
ここからは解説をしていきたいと思います。

解説

ここでのポイントは 3タイプに沿ったアプローチどこにリソースを集中させるのかです。

3 タイプに沿ったアプローチとしては、それぞれ 原則 として記載している部分がそれにあたります。
Intrinsic Load(課題内在性負荷)は開発しないといけないものだったりするので、できる限り整理した上で対応しましょうといったことになります。また、実際にやっていくタスクは小さく分解しながら進めるようにした方がいいでしょう。
Extraneous Load(課題外在性負荷)は本来やりたいことの外部の負荷になるので、なるべく減らしましょう。
Germane Load(学習関連負荷)は個人的に一番重要かなと思っております。例えばツールの理解や API の仕様把握、言語やミドルの理解などがあると思います。それぞれの技術の知識を蓄え、既存の知識と紐づいた時に新たな発見や気づきが生まれると思います。その結果、Intrinsic Load(課題内在性負荷)に対してベストな回答をしていけると感がられるからです。もちろん、拡大解釈してる気はするのですが、知識があるからこそ解決できる課題もたくさんあるため、積極的に投資をしていきたい部分になります。

ここからは個人的に思うそれぞれの負荷の対応の進め方を考えてみました。

まずは、Extraneous Load(課題外在性負荷)が解決可能であればこちらを解決しましょう。例えば、XX アプリを開発する必要があるが開発環境を毎回作ったり切り替えたりするのが大変といった場合は、Docker などでアプリが実行できる環境 or 開発環境を時間をかけてでも作ってしまう。その上で開発に入った方が後が楽になるといった感じです。よく言われる手作業で 30 分掛かるものを 3 秒で終わらせるために 3 時間掛ける話と似てるかもしれないです。

次に Intrinsic Load(課題内在性負荷) の分類を積極的にするのがいいと思います。もしかしたら情報を整理すると意外に作らなくてもいいものがでてくるからです。よくあるのは、依頼者と開発者で見ている視点が違うので、やりたいことがわかってもそこへのアプローチや前提情報が異なり話が平行線になってしまうパターンです。情報を整理する負荷というのは、その瞬間は負荷になりますが開発工程で正していくのはいくらアジャイルの開発であっても負荷になってしまいます。だからこそ開発初期のタイミングで依頼内容を分類し一番大切なことをやっていく必要があると思います。

Germane Load(学習関連負荷)こちらについては最後というよりは、エンジニアであれば日頃から技術についてはなんらかの方法でアップデートされていると思うので、業務課題や開発依頼と技術を紐づける部分のは閃きに近いものもあるので率先してやる必要はないかなと思ってます。

優先度をつけるとこんな感じでしょうか?
1.Extraneous Load(課題外在性負荷)
2.Intrinsic Load(課題内在性負荷)
3.Germane Load(学習関連負荷)

私自身業務を進める中で、このような順序で対応していくことが多いと思います。もちろん時間がない場合は、Extraneous Load(課題外在性負荷)を進めていくのは厳しいのですが、なるべくExtraneous Load(課題外在性負荷)を削減できるようにしています。

まとめ

個人的なまとめは下記のようになっています。

認知負荷とは

そこでの向き合い方(付き合い方)。

今回の記事は 認知負荷(Congnitive Load) を説明しながら、エンジニアの事象になんとなく合わせていくことをしました。認知負荷の最初の理解は、漠然と認知負荷は良くないことといった理解でした。ただ、解像度を上げてみると、認知不可にも必要なものと最小にしないといけないものがあることがわかりました。その言葉の意味を知らずに表面を利用すると間違った解釈をしてしまうなとあらためて気がつかされました。

個人的にはエンジニアの DX(Developer Experience)を改善することが長くいいものを作っていける重要な要素と考えています。いきなり最高の環境を提供はできないので、まずは開発と同じで現状把握をする中で、漠然として捉えていたことを明確な言葉で説明できる様にすることは非常に重要だと考えています。思考の整理だけでなく、漠然とした不安も少し払拭できるからです。不安を消しながら自らやっていくことを定めていけばある程度なんとかなるのかなと今回の zenn の記事を作成してる段階で感じました。

参考

Discussion