仕事が楽しくなる!? プロジェクト把握のためのコード読解のコツ
この記事について
この記事では、全てのエンジニアが必ず行う「コード読む」という行為について、その重要性とノウハウを説明します。
想定読者
以下のような方に、特に読んでもらいたいです。
- 若手のエンジニアさん
- ソースコードを読むことに苦手意識を感じる方
- 担当以外のソースコードをあまり読んだことが無い方
- 普段の業務にマンネリ感や伸び悩みを感じている方
この記事を書いたきっかけ
筆者の若手時代、優秀な先輩を見習い、色々と模倣していたのですが、最も効果が高かったのがプロジェクトのコードリーディングでした。
プロジェクトのコードリーディングは、以下のような理由から、とても大切なことだと考えています。
エンジニアの仕事において大切なことは「要望に対する実現可能性を判断し、現在のアーキテクチャに対し適切な形に落とし込んで実装すること」であると考えています。そのためには、コードリーディングによるプロジェクトの構造の理解が必要不可欠です。
新規プロジェクトや新機能を実装する場合でも、過去作の実装を知り・参考にすることはとても重要です。
図でまとめると以下のイメージです。
コードリーディングのメリット
また、コードを読むことに対し、苦手意識を持つエンジニアさんは一定数いる印象を受けます。若い頃の筆者もコードを読むのが苦手で、コツを知りたいと思っていました。
以上のような経緯から、コードリーディングをテーマとして記事をまとめたいと思った次第です。
具体的なやりかた 〜プロジェクト全体のコードベースを把握する方法〜
ここで説明するのは、プロジェクトの大意を掴むためのものですが、いくつかのテクニックは、詳細な実装を追う際にも使えます。
プロジェクトのディレクトリ構成に着目する
筆者の場合、最初にプロジェクトのディレクトリ構成を確認します。
「ディレクトリ構成を確認するだけで、コードの理解に繋がるのだろうか?」
と思う方もいるかもしれませんが、実はとても重要なものです。
ディレクトリ構成は、コードをどのようなモジュールに分けているかの見取り図に他なりません。ディレクトリ構成を見るだけで、コード全体の規模感、どのような機能がありそうか、どのようなレイヤに分かれていそうかが、何となく推測できます。
例えば、以下のような方法が理解に繋がります。
- ディレクトリ構成を、マインドマップなどに書き起こす(全部でなく、大きな粒度で構いません)。
- プロジェクトのコードを読みはじめたばかりの時期(例えば1〜2週間目)は、定期的にディレクトリ構成を確認し直す。
ディレクトリ構造
ディレクトリ構造を図にして、全体をイメージで捉える
プロジェクトで使われている主要なデータ構造を把握する
ディレクトリ構成と同じく重要なのが、プロジェクトで使われている主要なデータ構造の把握です。データ構造を事前に把握しておくと、プログラムを読むのがグッと楽になります。
理由は「プログラムの処理の大半は、データ構造に対する整形処理(データ構造のたらいまわし)」であるからです。 また、ソーシャルゲーム業界では、年々開発期間と費用が肥大化してきています。それに伴い、データ構造とそれを扱うプログラムは複雑化してきているため、データ構造の事前把握がより重要となります。
「データ構造は、プログラムによって処理されるあらゆるデータである」ということをイメージしてみると、実に色々なものがデータ構造であると見なせます。以下はほんの一例ですが、Unityのゲーム開発における例を示します。
- インゲーム用のAPIなど、比較的大きくて複雑なAPI
- キャラクターの3Dモデル
- Unityが提供するあらゆる機能
- 設定項目が多い・エディタ拡張を作り込んでいるMonoBehaviourやScriptableObject
データ構造をどう把握するかの方法は、該当のデータ構造がどのようなものかによって異なりますが、例えば以下のような方法が挙げられます。
- 該当のデータ構造を説明するドキュメントを読む(何を目的としたデータ構造なのか・何に/どのように使われるデータ構造なのか)
- どのようなパラメータを持っているのか大まかに把握する
- 具体例となる実際のファイルを読んでイメージを掴む
もちろん、全てのデータ構造を把握しなくても大丈夫です。小規模なもの・比較的分かりやすいものなどは省略しても良いでしょう。複雑なデータ構造ほど、事前に把握しておくメリットが大きいです。
データ構造を前もって把握しておくと、該当するプログラムを読むのがグッと楽になります。意識されたことがない方は、是非一度試してみてください。
RPGのデータ構造の一例
データ構造を言語化し、図として作成しておくと
複数人での作業も円滑に行うことができます
木を見ず森を見るように、大きなモジュール単位で読む
ディレクトリ構成把握・データ構造の把握が済んだら、ようやくコードを読み始めます。
といっても、現場のプロジェクトで使われるコード量は大きいです。一つ一つ丁寧に読んでいったら、なかなか大意の把握に繋がりません。そのため、はじめのうちは大きめなモジュール単位で読むように心がけてください(ここで言うモジュールとは、一つのディレクトリや一つの名前空間など、比較的大きなコード群を意味します)。
特に、モジュールの骨子を掴むために、はじめは以下のような読み方をしてください。
- クラス名や、publicなメンバ、その他重要と思われるメンバにだけ着目する
- ※クラス数だけでも膨大に存在するモジュールの場合は、一度に全て読もうとせず、段階的に読み進めていくと良いでしょう。以下に例を示します。
- 1回目:クラス名・クラス間の関係性の把握にのみ集中する
- 2回目:クラス内の主要なメンバに着目する
- 3回目:はじめて各クラスの実装を読んでみる
- クラス図やマインドマップを書きながら理解する
- 図を作成しながら読むと理解が深まります(重要なクラスだけで大丈夫です)
- この時クラス間の関係についても、図を作成しましょう。
- 継承か? 委譲か? 利用か?
- 1体1関係か?、1体n関係か?
- 図を作成すると、例えば以下のようなことが分かります
- 多くのクラスと関係を持つクラスが、(残念だが)特に責務が大きく・把握が必要なクラスだと推測できる
- どのようなデザインパターンが使われているか・どのようなクラス間関係かを把握できる
- 枝葉のクラスは無視する
- 全てのクラスを把握しようとしたらきりがありません。継承関係の末端にいるクラス、具体的な実装を受け持つだけのクラスは、はじめは無視して構いません。
- 開放閉鎖原則に則り、機能追加が可能なクラス
ex) 個々のキャラクターや個々のスキルなど、末端の機能を表現するクラス
- 開放閉鎖原則に則り、機能追加が可能なクラス
- 内部的な実装だけ受け持っており、外部のクラスとほとんど関係を持たないクラス
ex) C++におけるPimplパターン
- 全てのクラスを把握しようとしたらきりがありません。継承関係の末端にいるクラス、具体的な実装を受け持つだけのクラスは、はじめは無視して構いません。
- ※クラス数だけでも膨大に存在するモジュールの場合は、一度に全て読もうとせず、段階的に読み進めていくと良いでしょう。以下に例を示します。
コードを読む際に、併せて心がけたいこと
以上が、コードの全体像を把握するための読み方になります。
併せて以下のような工夫もおすすめします。
アウトプットしながら読む
何かしらのアウトプットをしながら読むと、記憶に残りやすいです。
あくまでコードを理解するためのアウトプットなので、丁寧にまとめることに意識を取られてしまうと本末転倒です。きれいなアウトプットを後に残すことは意識せず、その場の理解を深めることを重視して取り組んでください。
アウトプットの方法をいくつか例示します。人によって定着しやすい方法は異なりますし、複数の方法を組み合わせてみても構いません。色々と実践してみてください。
i) 紙に書く
前述したクラス図やマインドマップの作成や、実装上重要と思ったことをメモするなどの方法です。
この際、PCではなく、紙に書くことをオススメします。手書きの方が、ふと浮かんだ思考をスムーズに表現できるためです。
ⅱ) コードの作りを説明する
人に説明すると思考が整理されるタイプの人は、こちらの方法をおすすめします。
前述した筆者の先輩はこのタイプで、プロジェクトのコードがどのような実装になっているかを、周りのメンバーによく説明していました。
プロジェクトを担当するメンバー間で、お互いにプロジェクトの全体像や個々のモジュールについて説明し合うのも、理解が深まると思います。
ⅲ) IDEを開きながらやってみる
コードを書くのが好きな方は、こちらの方法をおすすめします。
実際にコードを書くようなイメージで、「ブロック単位でコメントを付与する」「よくない実装だと感じた箇所をリファクタリングしてみる」など、実際のコーディングのような感覚でアウトプットをしていきます。
また、動きがよく理解できなかった箇所に対し、デバッガーでブレイクポイントを貼り実際の値を確認してみるのも非常に有効です。
特にマップエディタのようなデータ構造とUIが大掛かりなシステムは
図を書くことをオススメします!
感想を持ちながらコードを読む
前述のアウトプットの項目と似ていますが、コードを読む際に何かしらの感想を持つのも、記憶の定着に有効です。
感想の内容は些細なものでも全く構いません。何も感想を抱かずに淡々とコードを読むのと、何かしらの感想を持ちながらコードを読むのでは、その後の定着が違ってきます。是非意識してみてください。
例)
自分の現在の理解度を確認する
- 「読み始めたが、まだ全然分からない」
- 「1週間経って再度読み直してみたが、前より理解できるようになった」
コードの構成に注目する
- 「主要なクラスはAとBあたりだろう」
- 「こういうデザインパターンを使っているんだ」
コードの第一印象に注目する
- 「名前空間やクラス名からして、あの機能で使われているコードだろうか」
- 「ここは改修依頼きても、きっと対応できそうだ」
- 「中々複雑なコードで、初見では頭に入ってこない」
要素技術に注目する
- 「知らない技術要素がいくつか使われている。併せて、関連する技術の勉強も進めてみよう」
- 「使ったことがないAPIが多いため、これらのAPI群も併せてメモしておき、後で調べてみよう」
- 「自分も後で使いそうなコードなので、スニペットとして盗んでおこう」
筆者固有の経験則
おまけで、コードリーディングする際の、筆者固有の経験則を紹介します。
(明確な根拠はありませんので、鵜呑みにしないでください)
仕様書を重視しない
一般的には、コードを読む前に仕様書の理解をすべきと言われます。しかし筆者の場合は、仕様書をいくら読んでもコードの理解が深まらず、むしろ悪化することもありました。
理由は、仕様書ベースでコードリーディングにあたると、仕様書と対応付けてコードを理解しようとするバイアスがかかってしまうためです。仕様書と対応付けてコードを読んでいても、コードの全体像や勘所は見えてきません。
また、仕様書とのコードは、必ずしも1対1対応の関係になりません。プロジェクトのコードは、仕様の変更に柔軟に対応できるように抽象化されていたり、歴史的な経緯で古い仕様のコードが残っていたりなど、仕様書と直接対応しているとは限りません。
そのため筆者の場合は、仕様書の読解よりも、実際の動作ベースで概要を掴む・コード読解の方に時間を充てることを重視しています。
※もちろん、機能追加やバグ修正をする場合は、正確な仕様の理解が不可欠です。 実作業に取り組む際は、必ず仕様書を丁寧に読解してください。
プルリクエストレビュー時は、関連コードも確認する
これも筆者の場合ですが、プルリクエストベースでのコード読解は、実力UPに繋がらないことが多かったです。プルリクエストは既にある機能に対し、何らかの観点でパッチを当てるような側面が強いからだと考えています。
そのため、時間に余裕がある場合は、プルリクエストのレビューをする際に、プルリクエストの該当部分だけではなく、関連する周辺のコードもまとめて読むようにすると良いです。その分だけ、コードに対する理解が深まりますし、実装者へのFBも良いものになります。
まとめ
プロジェクトのコードを読むことのメリットと、読み方を説明させて頂きました。この記事では、業務で担当しているプロジェクトのコードを読むことをテーマとしました。
しかし、読解対象のコードは、必ずしも担当されているプロジェクトのコードでなくても構いません。有名なOSSや、気になっているライブラリなど、業務に関係のないプロジェクトを読解対象にしても効果があります。
「知らない言語」「知らないフレームワーク」「経験したことが無い分野」など、読者の皆さんにとって遠いコードをテーマにした方が、より多くの学びを得られると思います。
この記事で紹介したような手法を試してみたことがない方は、是非実践してみて頂けると、筆者はとても嬉しいです。
はじめは、いつもより少し早く出勤する・少し早く業務を切り上げるなどして、浮いた時間でコードの読解をしてみてください。
数週間単位では効果が出づらいですが、数ヶ月単位で見ると実に色々な側面からの改善が見られると思います。
Discussion