@kyanny's blog

My thoughts, my life. Views/opinions are my own.

私のソースコードの書き方

note.mu

なるほど自分も同じような感じでやっているなぁ、と思った。もうちょっと詳しく書くと、

  • まず変更しようと思っている部分の周辺のコードを読んで、「ここらへんをいじればよさそう」と当たりをつける(当たりのつけかたにもいろいろあるのだが後述)
  • 土地勘を養ったところで具体的な変更の仕方を考える。必要に応じて紙に下手くそな図を書いたり、考えを箇条書きにしたり、実際にコードを試しに変更してみたりする
  • この方針でいけそう、と道筋が見えたらいよいよコードを書き始める。細かい単位でコミットするかどうかは場合によるが、少なくとも git add はこまめに行う(エディタの undo でせっかく書いたコードを失わないため)
  • 道筋が見えなかったり、プロトタイプ的に書いたコードが望み薄そうだったら潔く諦める。煮詰まっていることを自覚して、コーヒーを買いにいったり、オフィスの外を散歩したりして頭をリフレッシュさせる(歩いてる最中に別のアイデアが思いつくことも少なくない)
  • 無理のない設計でちゃんと動くものができたら仕上げに入る。テストを書いたり、マークアップを調整して見栄えを整えたり、など

だいたいこれが基本方針で、どんな種類のコードを書くときも概ねこういう感じ。それに加えて、新機能追加とバグ修正のどちらのコーディングか、またサーバサイド(Ruby on Rails)とクライアントサイド(Marionette.js)のどちらのコードを書くかによってアプローチが違ってくる。

サーバサイド & 新機能追加

できる範囲でテスト駆動開発のスタイルを取り入れる。が、無理はしない。実装後にテストを書くのも許容する。

仕事の場合、「正しいプログラミングプラクティスを実践すること」ではなく「要求を満たす製品を妥当な時間・妥当な品質で作ること」に対して対価をもらっているのだから、当然この優先順位になる。

サーバサイド & バグ修正

できる限りテスト駆動開発のスタイルを守る。バグを再現するテストで 1 コミット(push して CI を red にする)そのバグを修正する実装で 1 コミット(push して CI を green にもどす)この 2 コミットのコメントは極力丁寧に書き込む。必要に応じてリファクタリング。

とはいえ難しいこともあるので無理はしない。場合によっては実装まで終えた状態からテスト部分のみ 1 コミットとして先に push したりする。

以前はテストとバグ修正を合わせて 1 コミットにしていたが、これだと「本当にその修正で直ったのか?」を示せないので、あえて一回テストを落として証拠を残すスタイルに宗旨替えした。

クライアントサイド & 新機能追加

機能が一通り動くようになるまではテストのことは一切考えない。もちろんテストは実装が終わってから書く。このタイプの実装をするときが最も手戻り・やり直しが頻繁に発生するので、テスト駆動開発のスタイルでやろうとすると手間がかかりすぎるため。

サーバサイドのプログラムに比べて一つの関数の責務が多くなりがち、かつテストを書きづらいので、必要に応じてテストの書きやすさのためにプロダクトコードに手をいれることも許容する(必須ではない関数の切り出しを行ったり、必須ではない getter/setter を用意したり、など)。

クライアントサイド & バグ修正

できる限りテスト駆動開発のスタイルを守る。が、やっぱり難しいものもあるので無理はしない。その代わり、バグ修正のコミットのコメントは極力丁寧に書き込む。

必要に応じて、バグのデモンストレーション用に jsbin のような外部サービスで再現コードを実装して、それの URL を書き添えたりもする。エッジケースのバグの場合、プロダクトコード上でも発生するタイミング等がシビアなことがあるので、最小の再現コードで原理を説明したほうが理解しやすいため(そしてプロダクトコード上でそれに相当する変更というのは得てして意図がわかりづらくなるため)


「当たりのつけかた」について。いま名付けたが、だいたいこんな感じで使い分けている。もちろんいちいち「今回はエンドツーエンド型でいってみよう」などと考えたりはしない。

ボトムアップ型 ダイブイントゥ型

MVC アーキテクチャでいう Model の部分に着目して、いきなりピンポイントに飛び込んで当たりをつけにいくアプローチ。ビジネスロジック、というか単純な計算をしている部分を変更したいときなど、どこを変更すべきかすでにわかっているときにとる方法。

2016/07/19 10:39 更新: ボトムアップって感じじゃないな(上がっていない)むしろ直接飛び込む感じだな、と思ったので改名した。

エンドツーエンド型

ボトムアップ型と真逆で、システムのアウトプットから順にソースを辿っていって目当ての場所にたどり着くアプローチ。システムが複雑で当たりをつけられないときに、ソースコードの調査を進めつつ探索範囲を絞り込むために使う方法。具体的には、 JSON API のレスポンスデータから「このデータをシリアライズしてるのはこのクラスで、データを提供しているのはこのインスタンスで」のように一ステップずつ進んでいく。手間はかかるが闇雲にソースファイルを開いて回るよりも早く目標にたどり着ける。

リクエスト・エンドポイント型

ボトムアップ型とエンドツーエンド型の中間くらいのアプローチ。ピンポイントで明らかにはなっていないが、ある程度まではすでに絞り込み材料がある場合にとるアプローチ。具体的には API のエンドポイントやアプリケーションのルーティングなど、 URL とそれに対応するハンドラ部分を起点に探索していく。サーバサイドの変更の場合はもっぱらこのアプローチをとることが多い。

モジュール・ネームスペース型

機能を実装するモジュールや、プログラム内のネームスペースなどで絞り込んでいくアプローチ。複数箇所から共有されているコンポーネントなど、 URL から一意にたどり着きづらいが逆に機能名が特徴的なものの場合にとるやり方。 Marionette.Module を使って構造化しているクライアントサイドの変更の場合にこのアプローチをとることが多い。