1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
コードリーディングについて アリエルネットワークCTO 井上誠一郎 自己紹介 書籍 「P2P教科書」 「パーフェクトJava」 「サーバサイドJavaScript入門」 「パーフェクトJavaScript」 今回の講義 心構えや経験談が中心 抽象論になりすぎないように実践可能な「トライ」ページ 次回講義の予告 3月1日の予定 「Webアプリのアーキテクチャの歴史と進化」 専門用語多め 反応を比較して今後の講義の参考にします コードリーディング(1) 現場で重要なスキル 既存コードベースがある場合、書くコード行数は驚くほど少ない 学習と実務でのギャップ サンプルコードは短い コードリーディング(2) 既存コードを理解できないと デバッグできない 新機能の追加ができない 既存コードと同じコードを書いてしまう(無知ゆえのコピーコード) => 更に読みづらくなる悪循環 理解できないコードは悪 多少、汚いコードはOK 拡張性に乏しいコードもOK バグありコードもOK 動かないコード? たまにはある しかし、 なぜ動くか自分で説明できないコードを書くのはNG コードリーディングの副次的効果 読み手のことを考えて書くようになる => 読んで疲れるのは嫌になるはず => 読む時にラクをするため、書くときに(多少は)苦労を背負える => いわゆる直交性や凝集性のような良い指標に自然に到達する => 抽象化の罠に注意 可読性 読んで理解できるコードなら、機能を改善できる 読んで理解できるコードなら、バグも直せる 読んで理解できるコードなら、動作も速くできる プログラミング言語依存? 動的解析と静的解析の基本手法は共通 手続き型言語(オブジェクト指向言語を含む)であれば変わらない 言語や開発環境の進化とともにメタ知識が増える傾向(後述) メタ知識:コード以外から読み取るべき知識 手続き型って古い? 拡張性に乏しいコードになる傾向がある しかし、 古いと切り捨てるのが能ではない 手続き型コードの利点 普通の人には読んで理解しやすい コードのすべてに拡張性や変更容易性が求められるわけではない 上位に手続き型層を「意識的に」作るアーキテクチャ cf. PoEAAの「トランザクションスクリプト」 対象とするコード規模 中密度で大規模なコード 高密度(難しいアルゴリズムなど)の読み方は別スキル コードのスケール感 行数のオーダー 1000行 やる気があれば全部頭に入る 1万行 気合が必要だがほぼ頭に入る 10万行 普通の人が根性で読める限界 100万行 読み方のコツが必要。全部は頭に入らない 1000万行 ひとりの人間に把握は無理 地図のメタファー コードリーディングの土地勘 ソースコードの「どの辺」にいるかの感覚を養う 見知らぬ土地を歩き回る時 ランドマークを探す 大通りを基準にする 地名の標識を探す 地図の有無 地図(ナビ)があると効率的 地図と目の前の現実を付き合わせる作業は必要 コードリーディングにも地図が必要 地図なしで歩き回っていると道に迷う、疲れる しかし、 普通はコードと対応する地図はない => 自分で作るしかない コードリーディングにはどんな地図が必要? 経験談 Lotus NotesのUnixクライアントの開発経験 X Window SystemのAPIをラップしてMS WindowsのAPIをエミュレーションするライブラリ XlibのAPIを知っていたので簡単に読めた ラッパー系のコードは下位層を押さえると簡単に読める 世の中には結構多い(ソフトウェア開発は抽象化層の積み上げだから) サンドイッチ理論 モジュールが上位に提供するAPI モジュールが依存している下位API => 上下の両端を意識してコードを読む 利点: 終点を認識して読める (コードリーディングのアンチパターン: 深追いして道に迷う) サンドイッチ理論(2) 長年の論争 トップダウンに読む派 ボトムアップに読む派 # 設計でも同様の論争 サンドイッチ理論(3) 毎回、下層から読んでいくと時間がかかる 理解したら下を忘れるスキルが重要 頭の切り替え 教訓 レイヤ化は複雑さの中に一本の方向を作る コードの複雑さの中では比較的とっつきやすい構造 地図に例えると方角 => レイヤを意識しよう トライ 新人の作業アサインにラッパー系のモジュールを与える (上下境界が明解なモジュール) Lotus Notes本体の開発経験 全体で500万行以上 動的解析 一般的にはデバッガを使う(環境によってはprint文デバッグ) デバッガで止めてコールスタックを見る => とにかく効率的 はじめてのコード 何はともあれ動的解析をする => 地図がなくてもまず景色を見てみる => (たぶん用語などが不明で)行き詰まる => 静的解析に移る => ある程度、(頭の中に)地図ができたら、また動的解析 => 繰り返す 動的解析の実践 デバッガで止めてコールスタックを見る エントリポイントを知る => Webアプリは比較的簡単(HTTPを意識する) 動的解析の実践(2) オブジェクトのライフサイクル管理を読み取る => Webアプリは比較的簡単(非localはglobal, session, requestの3種に大別できるので) => オブジェクトのライフサイクル管理はGCがあると軽視されがちだが、意識するとコードの構造を読み取るのに役立つ (コードの書き手と読み手の間のギャップになりやすいから。 書き手は暗黙にライフサイクルを理解しているが、読み手は意識しないと読み取りにくい) トライ (もしなければ)デバッグ環境の構築文書を開発チームで用意する お勧めのブレイクポイント地点も文書化 => 新人に最初に読ませる UMLの用語を使うと シーケンス図などインタラクションに相当する情報は(普通に書くと)コードに陽に現れない # クラス図に相当する情報は現れる => インタラクション情報を静的に読み取りやすくする工夫 静的解析 Lotus Notes開発時代、ひたすらコードの断片と知見をメモ 重要そうな処理に当たりをつけて「処理中心」にコードを読むスタイル 静的解析(2) メモを取る - 分からなければ分からないことをメモる - 分かったことも忘れないようにメモる 幅優先的に読む - おおよその役割がわかっているコードであれば、必要がない限り深入りしない 静的解析のブレイクスルー APIで切っていく感覚 内部APIも含めて、モジュールやレイヤの境界を意識して「切っていく」感覚 データ構造を意識する感覚 処理中心からデータ構造中心にコードを読む オブジェクトのライフサイクルを意識 (オブジェクトの)集合に対する感覚 集合および集合に対する操作(列挙など)を意識(いわゆるコンテナやコレクションなど) 抽象化 境界を把握できたら頭から細部を追い出す => ブラックボックス化 => この抽象化こそが鍵 => 完璧に内部を知る必要はなく何をするかだけがわかればよい。知る必要が来る時まで探索は遅延 抽象化(2) 内部(仕組み)を知りたくてたまらない時は? => 読めばいい。ただ一段潜ったことを自覚する(この自覚がないと潜りつづける危険がある) => 知的好奇心を一概に否定したくはないが、全体像をつかんで位置づけを知ってから細部に潜るほうが効率的なことが多いので、多少の我慢は必要 トライ コードを読みながらテキストファイルにメモを取ってみよう ブレイクスルー(続) 公開APIのサンプルコードは、ムダが無く直接的なので、大きな概念をつかみやすい 名前も整理されている => ソフトウェアがサービスとして外部に提供する機能境界 => スケールアップしたサンドイッチ理論 レイヤ再び - 利用APIや利用フレームワーク(下から押さえる) - システムの提供APIを調べる(上から押さえる) - 単体テストのコードから - APIマニュアルから - サンプルコードから - 上と下の境界を意識して部品として把握する トライ 作っているソフトウェアの公開APIを作ってみよう (今ならWeb APIもお勧め) => 名前や概念を整理する良いきっかけになる リファクタリング 長いメソッドを下請け処理にわける(基本的なリファクタリング) - 手を動かして見えてくる世界もある - ただし開発フェーズを考慮すること(リリース直前のリファクタリングは自殺行為) - 「動いているコードを可読性のためだけに触るな」と怒られたら => 戦う or 逃げる(転職) - 下請け処理に分割した時の命名は重要 コトバとメタ知識 ソフトウェアが複雑化すればするほど コードに陽に現れない知識(抽象化された層の内部に隠蔽)が増える コードから読み取る知識とそうでない知識を早めに選別しないと人生が終わる コトバとメタ知識(2) コード以外から可能な限りメタ知識を仕入れる システム概要やユースケースを知る(そもそも、このソフトウェアは何か?) (コード以外の)メタな情報から高レベルな概念(アーキテクチャ)や用語(コード上の略称)を把握 内部スペック文書を読む メタ知識の例 HTTPを知らない人がTomcatのコードを読んでHTTPを理解 => 頑張ればできるが、徒労 => HTTPを理解してからコードを読むほうが効率的 トライ 自分が作っているソフトのユーザマニュアルを読む 用語集を作る 新しいプロジェクトに参加した場合: 0, (前提)Webアプリ 1. HTTPやURL、RDBの基本知識、Webアプリの一般的構造(MVCなど)を学習 2. そのソフト(システム)が何かを知る(コード以外から) 3. 使っているフレームワークやライブラリを知る 4. おおまかにコードの構造を眺める(命名規約やビルドの方法など) 5. デバッグできる環境を構築 6. 動的解析 ここまで土地勘を作るためのステップ 7. 利用フレームワークのサンプルコードを書いてみる (時間がなければ最低でもチュートリアルを読む) => フレームワークの境界を明確にする => ここから下に潜らない(ブラックボックス化) 8. モジュールの役割(責務)を調べる(言語化する) 9. エントリポイントを調べる 10. 重要データ構造を探す(e.g. アプリケーションコンテキストなど) 11. 手を動かす アンチパターン 頑張りすぎる(聞いたほうが速い) コードからメタ知識を読み取る 言語や慣習の知識不足 コードの書き換えを恐れる まとめに代えて 隣の芝生は青く見える... 単体テストが完璧なコード 見通しの良いコード 現実 他人の書いたコードがそんな簡単に理解できるはずがない 自分を取り巻くコンテキストを未来の読み手に伝える努力をしよう (今、当たり前のことが1年後には当たり前とは限らない) 読み取ったコンテキストを書き残して次に伝えよう |
Leave a Reply
You must be logged in to post a comment.
最近のコメント