κeenのHappy Hacκing Blog | Lispエイリアンの狂想曲

私がコードを書くときテストは書かない

ちょっとポエムというか自分語りを。会社の同期と話してて少し刺激されたので。あとはソースコードって実際のところどういうふうに書いていますか?|Rui Ueyama|noteにも刺激されて。 発端は同期が最近は何か書いてもイケてなくて後で丸っと書き直すことが多くてつらい、という話を始めたこと。 自分は、そんなの普通だろと返した。少なくとも自分の中では当たり前だった。 ふと考えてみたらそうじゃない人も沢山いる気がした。当たり前じゃない人に、自分の当たり前を喋ってみたら面白そうということで自分がコードを書くときにやることを語る。

コードを書き始めた時点では仕様は完全には固まっていない。アプリケーションの構成とかは決まってるけど、ソフトウェアの中身はほとんど何も決まっていない。 まずは手を着けやすそうな所から始める。最初は必ずHello Worldから。そしてmainの中にPoCを書いてイメージを掴む。 そこから一気に飛躍して、ディレクトリ構成を決める。mainは三枚下ろしにして各ディレクトリ下に入れる。これでアプリケーションの骨格が見える。ここまでトップダウン。この状態でアプリケーションを走らせるとPoCが動く。

次はボトムアップのフェーズで、骨格に肉付けしていく。1度に考えることが一箇所で済むように、順番を考えながら気儘にコードを加えていく。だいたいデータアクセス層のインターフェース、データアクセス層の実装のスタブ、サービス層のスタブ、メインルーチン、サービス層、くらいの順番。抽象すると必要そうなものを定義する、定義したものを使う、定義したものを実装する。多少型が違ってコンパイルが通らなくても気にしない。エラー処理やエッジケースは全部スタブのまま。コードのあちこちに FIXME が散らばる。条件分岐の枝は全ては書かない。頭のコンテキストスイッチで死んでしまう。集中したい枝以外はFIXMEで埋める。こんなもんかな、と思ったところでちゃんとコンパイルが通るように修正する。ここまでテスト無し、データアクセス層の実装無し。

ここからようやく永続データのスキーマやアプリケーションの仕様を決める。コードを見ればどういうデータにパターンでアクセスして、どういうエッジケースがあるかが分かるからサクサク決まる。

データのスキーマが決まったので最後データアクセス層を実装出来る。そしたら動くものが完成する。動いたものをポチポチやってみて実装が正しいことを確認する。

このまま細かい実装を詰めていくかというとそうならない。仕様を見て、コードを見て、イケてない所を全部書き直す。まずはインターフェースを変える、型を変える。意図的にコードを壊す。あとはコンパイルエラーで修正箇所が全て分かる。ついでにFIXMEも片付けていく。エラーが取れてアプリケーションが再度動くようになったら、リファクタの完了。納得のいくコードになっている筈。ここでようやく仕様を固定するためにテストを書く。全てのコードが出来てからテストを書くからホワイトボックスに近いテストになる。

どうしてこのようなプロセスを経るかと考えてみた。ポールグレアムも言っているように、私にとってのアプリケーションは、アプリケーションのドメインを記述するための巨大な言語(ライブラリ)と、それを使った非常に小さな実装からなる。 そして言語と実装の境界は何度も押したり引いたりしてようやく固まる。この手順は境界が固まるまでの過程だ。DSLを作るにはまずどのようなAPIがあれば便利か理解する必要がある。理解するのに一番手っ取り早い方法は一旦実装してみることだ。 ある人は言うだろう、「何を七面倒な。最初からちゃんと設計していれば手戻りがないものを。」と。違う。こういうときに私はよく詰将棋を引き合いに出す。 新聞の片隅にある詰将棋を解いてみる。新聞とにらめっこしてアレコレ考えても中々上手くいかない。 ところが将棋盤を引っ張り出してきて、パチパチ駒を動かしてみると簡単に解けることがある。ここは実は銀を持っていた、ここで馬が効くからこの手は指せない。 人間先を読むには想像力が足りなすぎる。目の前に現物がないと気付けないことが多い。だから先に実装する。一度作ってしまえば壊してもコンパイラが助けてくれる。

一度目でアタリを付ける。二度目で実線を書く。作って、壊して、また作る。

Written by κeen