全体として太一が感覚的に実践している事を論理的に説明しようと試みている為、
説明の粒度が適切でなかったり一貫性が無いように見える部分があるかもしれない。
普段やっているけども書ききれていない事も多分きっとある。
- コードを嗜む
- コードを学ぶ
- 武器を手に入れる
これは武器を手に入れるコードリーディングの話
- プロジェクトで戦う為の道具となる知識
- プロジェクト内における技能を示す為の道具となる知識
- プロジェクトのやり方を自ら決められる様になる為の道具となる知識
権限と責任がバランスよく割当てられた時モチベーションは最大化する
- プロジェクトリスク
プロジェクトをより良い状態にする為の基礎的な根拠となるのがコードである
コードが全てではないが適切に動作するコードが無ければプロジェクトは終わらない
これはつまり理想を体現する為の方法論では無く、目の前の問題を解決する事を最大の価値とするやり方である。
管理不能にならない限り少々ダーティなやり方でも許容するし、余り妥当でないコードが目の前に大量にある事を前提とするやり方である。
- 視覚
- エディタやIDEが出力する細かいアイコンに敏感になる
eclipseには非常に細かいアイコン芸の様な文化があるので敏感になるとより多くの情報を得る事が出来る - コードを高速にスクロールする事でコードを印象で把握する
- エディタやIDEが出力する細かいアイコンに敏感になる
- 聴覚
- クラス名やメソッド名を読み上げる事で聴覚を使う
- コードに対する印象を口に出す
- 自然言語による理解
- 静的読解
エディタのみを使うコードリーディング - 動的読解
デバッガを使うコードリーディング
クラス図の様なモデルによる理解は、感覚と論理の両方を満たす効率の良いやり方である可能性が高いので低コストに得られるのであれば積極的に利用する。
記述の厳密さに囚われる事無くスケッチレベルのクラス図や状態遷移図を手書きすると良い。
ある種のプロファイラを使うとコードの動作に基づくシーケンス図を自動生成してくれる。
脳の色んな部分を使う事で効率的にコードを読もう
理解はなだらかなグラデーションを描くものではあるが目安として以下の様なレベルを想定する。
- コードを見る
文字通り見るだけで細かく追わないし内容について考えないレベル - コードを読む
ある程度気を付けて読むが適切に理解している訳では無いレベル
オブジェクト間のインタラクションに余り気を使わない - コードを理解する
処理構造やライフサイクルを厳密に把握しているレベル
主要な部分に関しては暗記し、会議中脳内にコードを展開出来る状態
何度も俯瞰と仰視を繰り返し、まずはプロジェクト内にある全てのコードを見る。
次に全てのコードを読む。最後に全てのコードを理解する。
すぐに理解出来る部分があるかもしれないが、全てをすぐに理解しようとしてはいけない。
又、物事を完全に正しく理解出来ると考えない事。
問題解決や問題発見を主要な価値としてコードを読む為には要件の理解は欠かせない。
機能要件と非機能要件を適切に理解する事で、当該ソフトウェアにおける中心的な価値を持つ部分を把握する。
要件の難しさと実装の難しさは必ずしも対応付かないが、価値のある部分に大きなコストをかけられないプロジェクトには管理上のリスクがある。
ユーザインターフェース、データベースへのエントリ、他システムとの連携等、どの様なバウンダリが存在するのか要件レベルで把握する事である程度の優先順位付けが可能になる。
- プロジェクトが依存しているランタイムやライブラリ、ミドルウェアを把握する
- コードとして記述されるべき範囲をある程度把握する事
- 特に依存ライブラリの機能を大まかに把握する事でコードに対する理解が容易になる
- 開発構築する為の手順が適切にまとまっていないプロジェクトはスケールしない
- 開発環境の構築手順書を作るのは、プロジェクトに入ってすぐにプロジェクトに貢献出来る可能性が高い
- ビルド環境が一定水準以上自動化されていないプロジェクトは派生開発におけるリスクが高い
- ビルドの自動化はそれなりに特殊な技能であるし、その技能を持つ人は少数であるので出来るとプロジェクト内における自分の仕事を確保し易いかもしれない。
- テストの実行環境が一定水準以上自動化されていないプロジェクトは技術レベル及び管理レベルが低い
テストの自動化は単に技術だけの問題では無くテストデータの管理やCIサーバの運用等、
ファシリティマネジメントを適切に行えていなければ実現不能であり、
プロジェクト全体としての品質管理に対する理解やコストのかけ方を評価する指標となる
- 定量的指標を収集していないプロジェクトである場合は、それを収集出来る様にする事でプロジェクトに貢献できる。
特にコードの複雑度や行数はちょっとしたツールを使うだけで数値が得られるので最低限個人的には利用する。- テストカバレージ
- 静的解析レポート
- 動的解析レポート
- コードメトリクス
定量的指標を上手く使うと人間がやるべき事を減らせる
逆に人間がやるべき事を減らせない定量的指標を積極的に肯定しない。
- タスクの粒度に対する考え方を理解する
- 優先順位の付け方を理解する
- タスク割り付けの方向性を把握する
- 発生頻度の高いバグの種類を把握する
- 障害の多くは特定の機能やコードに集中する傾向がある
- 相対的にみて多くのバグを出す特定個人が存在する場合がある
- コミットの単位
- コミットログの書き方
- CIサーバやタスクトラッカによる自動コミット
- タグの使い方
- コミットが多い特定個人をプロジェクト規模に応じて複数名把握すると良い
- コードに関する質問先となる人物を探す
- コミットログを見ることでプロジェクトメンバーのモラルレベルを計る
- コミットログに規律が無いということ自体をきちんと把握することには意味がある
- 酷いログの中でも、まともにやろうとする人がいるなら、その人とは必要に応じて対話する
- エディタに求める機能
- シンタックスハイライト
スペースやタブ、改行等の非表示キャラクタを表示する
半角文字と全角文字の違いが明確に分かるフォントを使う
プロポーショナルフォントよりも等幅フォントオススメ
Windows環境でなければRictyオススメ - タグジャンプ
別のファイルにあるクラスや関数の宣言へのカーソル移動
eclipseの場合、クラス階層や呼出し階層を表示する事も出来る - ブックマーク
コードを読みながらメモ書きし、それを一覧から選択する事で戻ってこれるならどんなものでも良い - 自動フォーマット
機械的にフォーマットする事で統一感のあるコードを読む事が出来る 一定の水準で保障されている部分がある事でコードは読み易くなる
- シンタックスハイライト
- GREPツール
タグジャンプでは追いきれない部分を評価する為に必要
正規表現によるテキスト検索を適切に使いこなせる事でより高速にコードを理解出来る - SCMクライアント
コードの変更差分を追いかける事で理解出来る事柄もある
- 細かく詳細に特定の部分だけを理解するよりも広く浅く様々な部分を把握する事が大事
- コードだけでなくOSやミドルウェア等の環境に対する適切な理解がコードに対する理解を助ける
- 依存するプロジェクト外のライブラリを同時に読み進める事を常に視野に入れる
特にOSSのフレームワークを利用する際にはアプリケーションコードだけで機能が完結しないのでフレームワークのコードを必ず読む事。
- 依存するプロジェクト外のライブラリを同時に読み進める事を常に視野に入れる
- 仮定と検証を繰り返す事でコードに対する理解を深めていく
- 間違える事を前提に仮説を立て記録し、検証していく事でより高速に理解する事が出来る
- もし読んでもいない部分に関する仮説が次々と的中する様であれば、それ以上コードを読む意味は無い
- コードを高速に読む事に注力する。
- 少々ミスや抜け漏れがあっても良いのでプロジェクトのコード全体を何度も周回する事が大事
コードは速く読もうとしなければ速く読める様にはならない
- アプリケーションコードとテストコードが分離されているか?
- コード以外のマテリアルはどの様にして管理されているか?
- ビルドの結果はどこに格納される?
- IDEによる出力とビルドスクリプトによる出力は一致している?
- ミドルウェアにどうやってデプロイする?
- IDEによるデバッグデプロイとサーバにデプロイする際に使用するマテリアルに差分はある?
- Javaの場合、ディレクトリ構造と名前空間は対応付いている
- ディレクトリによって役割や責任範囲が分割されていなコードを読む時には相応の覚悟がいる。
- ミドルウェアとアプリケーションの接合面となるファイルを理解する事でコードをどこから読めば良いのか分かる事が多い
- 設定ファイルは開発と運用の接合面にもなる為、プロジェクトに対する広範は理解には欠かせない
- ログに関する設定があるなら、DEBUGやTRACEといった出来るだけ沢山ログが出る様に設定する
- web.xml, beans.xml, config.yml
- ミドルウェアにアプリケーションがデプロイされる場合、ブートストラップエントリが分からなくても良い
- その場合、ミドルウェアがアプリケーションをブートストラップする為のクラスを設定ファイル等から探す
- 名前空間によってアプリケーションアーキテクチャを把握する事が出来るケースが多い
- 要件の単位とモジュールの単位がどの様に対応付いているか把握する
余りにかけ離れている場合、理解が難しいので慎重に読み進める必要がある
その様なプロジェクトである場合、専用のドキュメントが作成されているかもしれない。
- コードを開いたらまずアウトラインが分かる程度の速度でスクロールする
- コードは文字の集合であるが絵として形や色合いを認識する
- 余りにカラフルな部分や、深い逆くの字型は中身を読まずにブックマークしておく
- コードを動かす事でまずはブラックボックス的に動作を把握する
- テストコードでも良いが出来る限りアプリケーションを動かす
- 何らかのエラーやバグによって動かない部分を理解しようとするのは、
自らがその問題を解決するのではない限り意味が無く辛い作業になるのでコストをかけるべきではない - 機能にそったブレークポイントを設定して動かす
- 入出力が為される部分にブレークポイントを設定する
例えば、ユーザからのインプットを最初に受け取る部分とデータベースへのエントリをトリガする部分
アプリケーションアーキテクチャに対する適切な理解があれば、適切なブレークポイントが設定できる
上手くブレークポイントで止まったらそこまでのコールスタックを見る
理解する段階にくるまで、変数の中身を詳細にチェックしたり、ステップ実行しなくてもよい
- 入出力が為される部分にブレークポイントを設定する
- 全てのクラスを二つに分類出来る訳では無い。抽象度の高低はグラデーションである。
- パッケージ名やディレクトリ名によってそれが為されていない場合には、ブックマークやワークセットの機能を使って分類する
- 継承階層を把握する
継承階層が深い場合、処理構造が立体的になる為、理解が難しい
デザインパターンが適用されている事をクラス名から類推出来る場合、理解をショートカット出来る可能性がある
クラス名やメソッド名(関数名)をどの様な基準で名前付けしているのかを把握する事で、効率的にコードを読み進める事が出来る。
ある種のフレームワークにおいては、名前付けルール(規約)によってアプリケーションが動作する。
- RailsやS2等
実装方針として契約が明確になる様な書き方をしていないとしても、オブジェクトには必ず契約がある。
契約が不明瞭であるとか、範囲が広すぎるものは理解が難しい為、ブックマークして後回しにする。
- メモリの確保から解放までのプロセスを把握する
- コードを見るだけの段階ではライフサイクルメソッドが適切に存在するかどうかだけを次々に確認するだけでよい - 例えば、ファイルのI/Oや、スレッドのロック、データベースのI/Oなどがある
- 機能の処理サイクルとオブジェクトのライフサイクルが一致していないものはその理由から理解していく
- 一つの機能を実現する為に、どれだけの数のクラスがどの様な粒度の役割を割り当てられているのか把握する
- コードを動かしながら読む事でオブジェクト間のインタラクションを把握する
プロジェクトの状況にもよるがコメントは動作しない部分であり、コメントに対する品質検査は殆どの場合行われないのでプロジェクトにおけるコードのコメントは基本的に無視する。
逆に、JDKのJavaDocの様に多数の人間に利用実績のあるプロダクトはドキュメンテーションコメントが適切に記述されている事が多いので積極的に読む。