
コードリーディングの考え方をまとめてみる

IT技術

概要
コードを読んできた経験を振り返ることで、読むことについて考えてみます。
記事の構成
最初にコードを読む目的を整理します。コードを読むのが良いとはよく聞くけれど、具体的になぜ良いのか、これまでの経験から見直してみます。
そして、自分なりに取り組んできたコードの読み方を紹介することで、自分自身のコードリーディングに関する考えを振り返ります。
読む目的・読み方について自分なりの考えをまとめることで、今後の自分自身・コードを読みたいと思っている方への参考になれば幸いです。
なぜコードリーディングが重要か
今までやってきたことを振り返ってみると、困難な問題を解決するのに役立ってきたのは、コードを読む力だと思いました。
コードを書くことももちろん大切ですが、仕事も趣味でもコードを読んでいる時間が多く占めています。
コードを読む力がなぜ問題解決に寄与しているのか、もう少し掘り下げてみます。
コードリーディングが活きたと感じた機会
具体的に問題解決に役立った場面をいくつか挙げてみます。
- 開発中に根深い問題が起きたとき、ひたすらコードを解読して原因を特定できた
- ライブラリのドキュメントだけではやりたいことを把握しきれなかったので、コードを直接読んで理解した
- フレームワークのコードを読んでいたことで、根拠を持った意思決定ができた
ほかにも例はありますが、まとめると、読む力を鍛えておけば必要な情報へアクセスする幅が広がると感じました。
知識を事前に得たり、その場で構築するには、やはりコードを読んだ経験を積み重ねていくのが大いに役立ちそうです。
コードを読むことを習慣にしたい理由
改めて、コードを読むことを習慣にするとどんな良いことがあるのか、整理しておきます。
業務で触れるコードはもちろんのこと、身近なライブラリやフレームワークのコードを読んでいけば、対象の理解を深めることができます。読んだことのないコードであっても、読み方を確立しておけばスムーズに読めそうです。
理解が深まれば問題が起きたときに対処するための引き出しが増えるので、より複雑な問題をより早く解決できるようになるはずです。
コードリーディングの難しさ
コードを読むことが重要であると書いてきました。たくさんのコードを読めば力もついてきそうです。
しかし、一口に読むといっても、何を・どう読めば良いか悩むところがたくさんあります。
以降はこれまでの経験から、コードリーディングの難しさにどう立ち向かってきたか振り返ってみます。
一個人の例から、コードを読むときの参考になれば幸いです。
何を読むか
まずは題材を決めるところから始めたいです。読むものが決まってしまえば、最終的には読み続けさえすれば力をつけることができます。
ここでは、何を読むのがよいか考えていきます。
鉄板のWebアプリケーションフレームワーク
すぐに効果が得られそうなのは、業務で身近なものです。
私の場合は、Webアプリケーションフレームワークの理解を深めたいと思っていたので、ここから手を出し始めました。
いきなりフレームワークはハードルが高いという方も、以下の書籍がおすすめです。読み進めていけばコードを読むモチベーションが高まってくるはずです。
参考
いくつかWebアプリケーションフレームワークのコードに手を出してきましたが、ブログでも何記事か書いている通り、Laravelは取っ付きやすくておすすめです。
有名どころのフレームワークは種類はどうあれ、たくさんの人の手でメンテナンスされているので、読み方を確立する上でも、参考書とする上でも適していると思いました。
痒い所に手が届くライブラリ読解
フレームワークだけでなく、ライブラリのコードをつまみ食いしてみるのも楽しいです。
シンプルなライブラリであれば、時間をかけずにサクサク読めるので、とりあえず気になったら手をだすぐらいの手軽さで始められます。
普段業務で使っているものはもちろんのこと、HTTPクライアントやロギング関連のライブラリから手を出すと、身近な技術の理解を深めることができます。
また、どちらかと言うとフレームワーク寄りですが、Jestなどのテスティングフレームワークのコードも覗いてみると、テストコードを書くときの幅が広がるのでおすすめです。
補足: フロントエンドの難しさ
フロントエンドを主に触っている方は、Next.jsなどのフレームワークのコードを読んでみたくなるかもしれません。
ただ、Next.js本体のコードはあまりにも難易度が高いので、バックエンド側のWebアプリケーションフレームワークから基礎を固めた方が良いかもしれないです。
実際に私も一度手を出しましたが、複雑すぎて挫折しました(;
(Reactもめちゃくちゃ難しかったです…)
どう読むか
ここからは、コードの読み方を考えます。ある程度数をこなして自分に合ったやり方を模索するのも良きですが、ひとまず私が実践している読み方を紹介してみます。
過去にけっこうな量のソースコードを読んだので、そのときの経験も踏まえてどう読むとつらくないか整理してみます。
参考
読むときのツール-GitHub Codespaces
フレームワークやライブラリのコードを読むときは、GitHubが提供しているCodespacesという機能をずっと使っています。
参考
読みたいリポジトリを表示し、「.」キーを押すだけでエディタが立ち上がるので、ちょっと読み進めたいときにもすぐに始められます。
読む目的を定める
一回のコードリーディングですべてを把握しようと思うと、とても疲れてしまいます。
(LaravelのHelloWorldの処理を追う記事は本当に大変でした…。)
ですので、コードを読むことで何を知りたいか、目的を定めることが重要です。
目的があると何がうれしいか
ゴールを決めていると、関係するところだけに集中できます。
コードは膨大なファイルや関数・クラスなどで構成されているので、知りたいことを定めることで、どれに手を出せば良いか見えてくるはずです。
緩急をつけて大枠を掴む
手を出すものを決めた後も、一行ずつじっくり理解する読み方と、欲しいものを探しに行く読み方を使い分けていきます。
コードは個別の処理はもちろんのこと、お互いが連携することでも機能しているので、機能同士の繋がり・機能の詳細から理解を組み立てていきます。
もちろん一回読んだだけでは到底理解できないので、一度読んだものもざっくり読んだり、ときにはじっくり目を通しながら、理解を偉られるまで格闘します。
実践-PHPUnitソースコードリーディング
さて、コードの読み方をざっくりと整理してきました。
理論だけでは分かりづらいので、実際のコードから読み方をたどっていこうと思います。
題材にはテスティングフレームワークのPHPUnitを選びました。じっくり読むのは今回が初めてなので、普段どんなやり方で理解を構築しているのか再現してみます。
ゴール
とてもよく使うメソッドassertSame()
が何をしているのか理解することをゴールに見据えます。
これは、引数に指定した値が等価であるかをもってAssertionを実現するメソッドです。
入り口
まずはGitHub Codespacesのエディタを起動させます。リポジトリのURLで「.」キーを押すことで起動します。
色々ファイルはありますが、対象のクラスはPHPUnit\Framework\TestCase
なので、該当のファイルにいきなり飛んでしまいます。
見たいメソッドはassertSame()
です。このような場合、下図のようにメソッド定義を検索から直接探してしまいます。
今回は見つからなかったので、継承クラスからメソッドを探し出しました。
ここでも入力と出力をちらっと見る程度で、関係が深そうなところまで流し見していきます。
より具体的には、クラスとメソッドの名前に着目しながら見たい処理を実現していそうなところまで、ひたすら呼び出し階層を掘り進めます。
見たいところ
クラスやメソッドを掘り下げていくと、Constraintクラスを継承したIsIdenticalクラスのevaluate()メソッドにたどり着きます。
等価比較のAssertionを実現する名前を表していそうなので、少しゆっくり読んでみます。
1// src/Framework/Constraint/IsIdentical.php
2/*
3 * Evaluates the constraint for parameter $other.
4 *
5 * If $returnResult is set to false (the default), an exception is thrown
6 * in case of a failure. null is returned otherwise.
7 *
8 * If $returnResult is true, the result of the evaluation is returned as
9 * a boolean value instead: true in case of success, false in case of a
10 * failure.
11 *
12 * @throws ExpectationFailedException
13 */
14 public function evaluate(mixed $other, string $description = '', bool $returnResult = false): ?bool
15 {
16 $success = $this->value === $other;
17
18 if ($returnResult) {
19 return $success;
20 }
21
22 if (!$success) {
23 $f = null;
24
25 // if both values are strings, make sure a diff is generated
26 if (is_string($this->value) && is_string($other)) {
27 $f = new ComparisonFailure(
28 $this->value,
29 $other,
30 sprintf("'%s'", $this->value),
31 sprintf("'%s'", $other),
32 );
33 }
34
35 // if both values are array or enums, make sure a diff is generated
36 if ((is_array($this->value) && is_array($other)) || ($this->value instanceof UnitEnum && $other instanceof UnitEnum)) {
37 $f = new ComparisonFailure(
38 $this->value,
39 $other,
40 Exporter::export($this->value),
41 Exporter::export($other),
42 );
43 }
44
45 $this->fail($other, $description, $f);
46 }
47
48 return null;
49 }
このときも一行ずつ読むのではなく、if文のブロック単位など大きな塊で捉えます。
すると、
- 成功したときはbool値trueを返していそう
- 失敗したときはfailメソッドを呼び出していそう
といったことが把握できます。
成功したらbool値から後片付けをしていそうだと推測できるので、あとは失敗したら何をしているのか気になります。
ということで、failメソッドの定義もちらっと覗いておきます。
1// src/Framework/Constraint/Constraint.php
2/*
3 * Throws an exception for the given compared value and test description.
4 *
5 * @throws ExpectationFailedException
6 */
7 protected function fail(mixed $other, string $description, ?ComparisonFailure $comparisonFailure = null): never
8 {
9 $failureDescription = sprintf(
10 'Failed asserting that %s.',
11 $this->failureDescription($other),
12 );
13
14 $additionalFailureDescription = $this->additionalFailureDescription($other);
15
16 if ($additionalFailureDescription !== '') {
17 $failureDescription .= "\n" . $additionalFailureDescription;
18 }
19
20 if ($description !== '') {
21 $failureDescription = $description . "\n" . $failureDescription;
22 }
23
24 throw new ExpectationFailedException(
25 $failureDescription,
26 $comparisonFailure,
27 );
28 }
これも大まかにみてみると、失敗の詳細を表現するメッセージを組み立てたら例外を投げていそうです。
理解の整理
大まかに知りたいことは把握できたので、ここでコードリーディングは打ち止めにします。
ここから重要なのは、得られた情報から理解を組み立てられたか見直すことです。
今回の場合、PHPUnitのAssertionはTestCaseクラスが所有しているConstraintクラスが担う。Constraintクラスは等価比較など期待の検証が責務で、検証が成功していたら処理を打ち切る・失敗していたら例外を送出することで失敗を伝播させる。
こんな感じで処理の流れと、関連が深い処理が何をしているのか言葉にしてまとめます。もし流れが繋がらなかったり、言葉にする中で詰まった場合はもう一度読みに行きます。
例えば成功していたら処理を打ち切っているだろうけれど、もっと知りたいから掘り下げてみる。クラスの所有・継承関係が曖昧になってきたからリトライ。といった具合です。
振り返り
非常にざっくりとではありますが、PHPUnitを題材にどのようにコードを読んでいるか見てきました。
一行ずつは読まず、流れを掴みながら知りたいことを理解するのに努めていました。
流し読みした程度でも、普段よく使うメソッドが裏で何をしているのかなんとなく見えてきたはずです。
また、等価比較以外のAssertionだったりsetUpやtearDown()などもどの辺りのコードを見れば読み取れそうか、大まかな地図も得ることができました。
そして、PHPUnitのコードを読んでいくのは楽しかったので、ほかのメソッドも自ずと読みたくなりました。
経験を積むことで身に付けたい力
今回は私のやり方を紹介してきましたが、コードを読むことで目指したいのは、自分なりに理解をするために効率が良いやり方を身につけることです。
理解を構築するのに慣れてきたら、膨大なコードから問題の解決に必要な情報を効率よく集められるようになります。
これを実現できれば、コードに起因する問題を解決する力も高まってきそうです。
楽しく知識を得ながら問題解決力を育てるのにコードリーディングは最適なので、ぜひ習慣化させたいです。
まとめ
本記事では、コードリーディングについて掘り下げてきました。
振り返ってみるとまだまだ読む量が足りないと感じたので、もっと読んで強くなっていきたいです。
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ

強くなりたい。