はじめに
こんにちは、株式会社estraでエンジニアをしています。福地です。
開発のプロジェクト管理をしている中で、参画後に仕様理解に躓く人とそうでない人がいることを感じており、その理由を言語化してみました。
仕様がドキュメントとして残っていないことはまず問題ですが、ない場合は必ずと言っていいほど、コードリーディングをして、仕様を理解することになります。
「他の人はサッとコードを読んで理解しているのに、自分は何時間かけてもシステムの全体像が掴めない」
この記事を見る人も、1回くらいはあるのかなと思います。
もちろん僕もあります。
そういった方向けに記事を書いてみましたので、ぜひ最後まで読んでいただけると嬉しいです。
この記事で扱うこと
- コードリーディングに躓くと躓かない人の認知科学的な違い
- 認知負荷を段階的に下げるコードリーディング
- AI を活用した認知負荷の軽減のヒント
前提知識
この記事を読むにあたって、特別な前提知識は必要ありません。
何かしらのプログラミング言語でコードを書いた経験があれば十分です。
躓くと躓かない人の認知科学的な違い
まず、コードを読むのが遅くなってしまう理由を脳の仕組みから知っていきましょう。
理由がわかれば、どうアプローチしたら良いかイメージしやすいかと思います。
コードを読むとき、脳の中で何が起きているか
コードを読むとき、私たちの脳では3つの記憶システムが連携して働いています。
日常的なものに例えると、次のようなイメージです。
| 記憶システム | たとえ | 役割 |
|---|---|---|
| 長期記憶 | 本棚 | 過去に学んだ知識や経験の蓄積。大量に保管できるが、取り出すには手がかりが必要 |
| 短期記憶 | 手のひらのメモ | 目の前のコードから読み取った情報を一時的に保持する。容量は2〜6個程度で、30秒ほどで消える |
| ワーキングメモリ | 作業机 | 短期記憶の情報と長期記憶から取り出した知識を組み合わせて「考える」場所。机の広さには限りがある |
つまりコードを読むとは、次の3つを同時にこなす作業です。
- 短期記憶に目の前のコードの情報を載せる
- 長期記憶から関連する知識を引き出す
- ワーキングメモリ(作業机) の上で両者を組み合わせて意味を理解する
ここで重要なのは、作業机の広さには限りがあるということです。
一度に載せられる情報量を超えると、脳は処理しきれなくなります。
この「脳が処理しきれない状態」を認知負荷が高いと呼びます。
躓かない人はどうしているのか:チャンキングの力
では、躓かない人はなぜ同じコードをサッと読めるのか。
その秘密はチャンキングにあります。
チャンキングとか仰々しい名前を出しましたが、要は複数の情報をひとつの「かたまり(チャンク)」としてコードを見ているということです。
たとえば、次の PHP コードを見てください。
$orders = Order::with(['user', 'items.product'])
->whereHas('user', fn($q) => $q->where('plan', 'premium'))
->where('status', 'pending')
->where('created_at', '>=', now()->subDays(7))
->orderByDesc('created_at')
->paginate(20);
躓かない人の読み方(チャンキングあり)
「直近7日間・未処理の注文を、プレミアムユーザーに絞り、関連データ(ユーザー・明細・商品)を一括取得してページネーションしてんな。」
-> 1チャンク
まあ、ここまでじゃなかったとしても、一旦注文データと一緒に注文に関連する(ユーザーと明細と商品のデータ)をまあ取ってきてんだな。とか
まあこれくらいにおさめたりしてます。
躓く人の読み方(チャンキングなし)
・with(['user', 'items.product']) って何? Eager Loadingって何?
・whereHas って where とどう違うの?
・fn($q) => ... って何の構文?
・now()->subDays(7) って何を返すの?
・paginate(20) って何を返すの?LaravelのPaginatorって何?
→ 5〜6個の情報が同時に作業机に乗り、すぐいっぱいになる
もちろん、今回はphpという言語特有のメソッドを知らないという技術能力不足があるのは前提ですが、そんなのはその時調べればいいんです。
そのあと忘れてもまた同じ問題が発生した時に、また調べて、あれあの時使ったなと思い出すものです。そうやって技術観点は長期記憶に蓄積されていきます。
勘違いしないでほしいのは、躓かない人が速いのは「頭が良いから」ではなくて、単に長期記憶に大量のコードパターンが蓄積されていて、チャンキングが効くからです。
つまり、躓く人がコードを読むのが遅いのは能力の問題ではなく、経験の蓄積量の問題です。(もちろん知識もありますが気合いで調べていきましょう)
経験を積めば、あなたの脳にもチャンクが増えていきます。
認知負荷が高くなるもうひとつの原因:ビジネスドメインとプログラミングの混在
コードが読めないとき、原因を切り分けるとふたつに分かれます。
- プログラミングの知識不足: 言語の構文、フレームワークの使い方、デザインパターンがわからない
- ビジネスドメインの知識不足: そのコードが解決しようとしている業務の意味がわからない
躓く人は、この両方を同時に理解しようとして、焦っているケースを見かけます。
「コードの構文も追いつかないのに、業務ロジックの意味も考えなきゃ……」となると、認知負荷は上がりますね。
まずは「今つまずいているのはどちらか?」を切り分けてみてください。
- プログラミングの知識不足なら → 言語のドキュメントや入門書で構文を確認する。
- ビジネスドメインの知識不足なら → 仕様書を読む、チームメンバーに業務の流れを聞く。
認知負荷を下げるコードリーディングの4ステップ
一度に処理する情報量を制限し、段階的に進める。
これを実現する4つのステップを紹介します。まあここまで厳密にやると逆に開発生産性が落ちるとかあるんで、適宜自分なりに調整してください。
- 探索 — コードベースの全体像を把握する
- 転写 — 読む計画を立てる
- 理解 — コードを自分の言葉で説明する
- 増強 — 理解を「使える知識」に変える
躓く人が自分自身でこの4つの分解を意識して進めれば、独力でもコードリーディングの壁を乗り越えられます。
4つのステップは、段階的に認知負荷が上がる設計になっています。
だからこそ、この順番で進めることに意味があるのです。
ステップ1 — 探索: コードベースの全体像を把握する
目的: いきなりコードを読み始めるのではなく、まず「地図」を手に入れます。
やること:
- ディレクトリ構成を眺めて、プロジェクトの大まかな構造を把握する
- README やドキュメントがあれば目を通す
- 主要なモジュール・パッケージの関係性をざっくり掴む
たとえば、次のような Web アプリケーションのディレクトリ構成を見たとします。
my-laravel-app/
├── app/
│ ├── Http/
│ │ ├── Controllers/ # リクエストを受け取る層
│ │ ├── Middleware/ # ミドルウェア
│ │ └── Requests/ # バリデーション
│ ├── Models/ # Eloquent モデル
│ ├── Services/ # ビジネスロジック層
│ └── Repositories/ # データベースアクセス層
├── routes/
│ └── web.php # ルーティング定義
├── tests/
├── README.md
└── composer.json
Laravel を使ったことがあれば、この時点で「リクエストは routes → Controllers → Services → Repositories の順に流れそうだな」と予測できるかもしれません。
まだそこまでわからなくても大丈夫です。
大事なのは、コードの中身を読まなくても、ディレクトリ構成だけでプロジェクトの雰囲気を掴めるということです。
「全部読まなきゃ」と思って最初からコードに潜ってしまうのは、よくある失敗です。
探索のステップでは「コードの中身」は読まなくて大丈夫で、ファイル名とディレクトリ名だけで全体像を掴むことに集中しましょう。
ステップ2 — 転写: 読む計画を立てる
全体像が掴めたら、自分のタスクに関係するファイルをざっくり絞り込んでから、読む計画を立てましょう。
目的: 何をどの順番で読むかを整理し、ワーキングメモリの負荷を外部に逃がす。
やること:
- 読むべきファイル・クラス・メソッドをリストアップする
- 処理の流れに沿った順番で並べる
- 「ここまで理解できたらOK」というゴールを設定する
たとえば「ユーザー登録の処理を理解したい」なら、次のようなメモを作ります。
【ユーザー登録の処理を理解する】
読む順番:
1. AuthController.php の register メソッド(エントリポイント)
2. AuthService.php の createUser メソッド(ビジネスロジック)
3. UserRepository.php の save メソッド(DB保存)
ゴール: リクエストを受けてからDBに保存されるまでの流れを説明できる状態
「計画を立てずに目についたコードを片っ端から読んでしまう」のは非常によくある失敗です。
紙やメモアプリに書き出すだけで、頭の中の情報が外部に逃げて作業机が広くなったような効果が得られます。
タスクの範囲ごとの仕様を理解するもよくあります。(全体像はまだ不透明だが、この機能だけは理解できてるみたいな)
ステップ3 — 理解: コードを自分の言葉で説明する
計画に沿ってコードを読み進めたら、次は「本当に理解できたか」を確認するステップです。
目的: コードを「読んだ」で終わらせず、「理解した」状態にする。
やること:
- メソッドやクラスの処理内容を自然言語で要約してみる
- 「このメソッドは〇〇を受け取って△△を返す」のように一文で説明する
- 変数の役割を書き出す、処理の流れを図示する
たとえば、次の PHP コードを読んだとします。
public function processOrder(int $userId, array $cartItems): Order
{
$user = $this->userRepository->findOrFail($userId);
$totalAmount = collect($cartItems)->sum(
fn($item) => $item['price'] * $item['quantity']
);
if ($user->point_balance < $totalAmount) {
throw new InsufficientPointsException();
}
$order = DB::transaction(function () use ($user, $cartItems, $totalAmount) {
$order = Order::create([
'user_id' => $user->id,
'total_amount' => $totalAmount,
'status' => 'pending',
]);
foreach ($cartItems as $item) {
$order->items()->create([
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'unit_price' => $item['price'],
]);
}
$user->decrement('point_balance', $totalAmount);
return $order;
});
event(new OrderPlaced($order));
return $order;
}
このとき、自分の言葉で要約してみます。
processOrder メソッド:
・ユーザーIDとカート内商品の配列を受け取る
・まず合計金額を計算する(collect()->sum() を使っているが内部でどう動くかは不明)
・ユーザーのポイント残高が足りなければ例外を投げる
・DB::transaction の中で:注文レコードを作成 → 明細を1件ずつ insert → ポイントを減算
・トランザクション完了後に OrderPlaced イベントを発火
・作成した注文を返す
言語化してみて浮かび上がる「理解できていない部分」の例:
・collect()->sum() に関数を渡せるのはなぜ? アロー関数とは?
・DB::transaction が失敗したとき、ポイントの減算はロールバックされる?
→ つまり「なぜトランザクションがここにあるか」を説明できるか?
・OrderPlaced イベントはどこで受け取られる?Listener は何をしている?
・findOrFail はユーザーが存在しない場合どうなる?
言語化してみると、理解できていない部分が浮かび上がります。それが次に調べるべきポイントです。
コードを読んで「なんとなくわかった」と思ったら要注意です。自分の言葉で説明できるか試してみてください。
説明できなければ、それはまだ理解できていないサインです。
ステップ4 — 増強: 理解を「使える知識」に変える
最後のステップです。理解した内容を「読んで終わり」にせず、手を動かして定着させます。
目的: 実際にコードを書くことで、長期記憶に定着させる。
やること:
- 既存のクラスに小さな機能を追加してみる
- 追加する前に計画を立てる(「どこに」「何を」「なぜ」)
- コードにコメントやドキュメントを書いて、理解をアウトプットする
たとえば、先ほどのユーザー登録処理を理解したなら、次のような小さなタスクに取り組んでみます。
- 「ユーザー名の重複チェックも追加してみよう」
- 「パスワードの強度バリデーションを追加してみよう」
追加する前に、計画を立てます。
【ユーザー名の重複チェックを追加する】
どこに: AuthService.php の createUser メソッド内
何を: email の重複チェックと同様に、name の重複チェックを追加
なぜ: 同じユーザー名での登録を防ぐため
変更箇所: UserRepository に findByName メソッドも必要
「理解しただけで満足してアウトプットしない」のが、このステップでのよくある落とし穴です。
「読んだコードに対して1つは何かアウトプットする」 をルール化してみてください。
コメント追加、テスト追加、ドキュメント更新など、小さなことで構いません。
このステップを繰り返すことで、長期記憶にパターンが蓄積されていきます。
複数の情報を「かたまり」として扱う力(チャンキング)は、まさにこの蓄積によって育つと思っています。
増強を重ねるほどチャンクが増え、いつの間にか躓かない人の読み方に近づいていくのです。
AI を活用して認知負荷をさらに下げる
ここまで紹介した4ステップは、自分の力だけでも実践できます。
しかし、AI を「認知負荷を下げる相棒」として活用すると、各ステップをさらにスムーズに進められます。
各ステップで AI にどんな質問をすればよいか
4つのステップそれぞれで、AI に投げかけると効果的な質問の例を紹介します。
| ステップ | AI への質問例 |
|---|---|
| 探索 | 「このプロジェクトの全体構成を教えて」「主要なモジュールの役割は?」 |
| 転写 | 「この機能を理解するために、どのファイルをどの順番で読めばいい?」 |
| 理解 | 「このメソッドは何をしている?」「なぜここでこのパターンを使っている?」 |
| 増強 | 「このクラスに〇〇機能を追加するとしたら、どこを変更すべき?」 |
AI はコードベース全体を俯瞰したり、特定の処理の意図を説明したりすることが得意です。自分ひとりで悩む時間を減らし、認知負荷の高い作業をサポートしてもらう使い方が効果的ですね。
AI 活用の注意点
ただし、AI の使い方には注意が必要です。
AI の説明を読んだだけで「理解した」と思わないこと。
まあ、これは当たり前ですね。
AI が出力した説明が正しいとは限りませんし、読んだだけでは自分の知識にはなりません。
必ず自分でコードを動かして確認してください。
AI は 「理解を代替するもの」ではなく「理解を助けるもの」 です。
「自分の言葉で説明する」をスキップしてしまっては、長期記憶への定着が弱くなります。
おすすめは、AI に聞いた後に自分の言葉で要約し直すステップを挟むこととかかなと。
「AI はこう説明してくれたけど、自分の理解では〇〇ということだな」と言い換えとかしてみると、理解の定着度は大きく変わると思います。
まとめ
この記事では、コードリーディングにおける躓かない人と躓く人の違いとアプローチについて簡単にまとめたわけですが
- 「能力」ではなく、長期記憶に蓄積されたパターンの量
- チャンキングが効かない躓く人は認知負荷が高い状態でコードを読んでいる。だから遅くて当然
- 「ビジネスドメイン」と「プログラミング」の知識は分けて攻略する
- 認知負荷を下げるには、一度に処理する情報量を制限し、4つのステップ(探索→転写→理解→増強)で段階的に進める
- AI も「認知負荷を下げる相棒」として賢く活用しよう
この4ステップを繰り返すことで、長期記憶にパターンが蓄積され、チャンキングが効くようになっていきます。
やっていくうちに慣れますので、焦らず頑張っていきましょう。
参考
- プログラマー脳 〜優れたプログラマーになるための認知科学に基づくアプローチ(フェリエンヌ・ヘルマンス 著、水野貴明 訳、秀和システム)