2002年、当時設立したばかりの会社に入り、何もない状態から、コンテンツとシステムを作り続け8年が経った。日々、試行錯誤しながら、それなりに会社も大きくなり、まだ、大成功とは言えないけど、それなりにうまくやってきたつもりだ。
しかしながら、その8年という短くはない時間の中で、色々な課題や問題が発生し、その時々正しい選択をしてきたつもりだったけど、反省点も多い。もう一度スタートアップに参加するとしたら、やり直したいところや、もっと早くこうしていれば良かったというところがたくさんある。
そんなわけで、次の挑戦のときに忘れないように、また、もしかして誰かの参考くらいになればと思い、メモっておくことにした。1
まず、反省点の前に、何をやっているのかというのを簡単に。
ビジネスとしては、英語e-learningのWebサービス(ネットを使った英語のお勉強)をASPな形で、企業や大学などに提供している。開発はすべて社内で行ない、データセンタに自前のサーバを持ち運用している。
Web業界の花形(?)であるB2Cではないので、そっちだと違ってくることも多いかもしれない。近い将来B2Cもやりたいのだけどね。
社内での自分の役割としては、入社以来、技術的なところを一通り見させてもらっている。開発から保守、運用、ユーザサポートのヘルプといったシステム周りはすべて。もちろん自分でプログラミングもする。また、システム側の人材の確保なども行っている。
そんなわけで、下記のことも「純粋な一(いち)プログラマ目線」なことや「マネージャ目線」やその他色々な目線が入り交じっているので、その辺はそういうことで。マルチロールで幅広くできるのもスタートアップの楽しさの一つでもある。
では、以下に反省点と課題をずらずらと挙げてみる。
システム開発についての反省点
1. 丁寧に正しく作ろう
一つだけ選ぶとしたらこれ。これがすべてにつながってくる。スタートアップ企業にとって、スピードが大切というのは間違いないのだけど、目先のスピードのために、色々なことを犠牲にしてしまっていたことがあった。正しく丁寧に作ることが中長期で考えるとスピードにもつながる、ということを頭で理解しつつも、つい手を抜いてしまっていた。
まず、「今必要でないことはやらない – YAGNI 」ということを常に考えよう。そして、その上で「今必要」となったものは全力で丁寧に正しく作ろう。
丁寧に正しく作られたアプリケーションは拡張がしやすくメンテナンスもしやすく不具合も起きづらい。当然のことを当然のようにやることがとても大切。
自社で、自社のシステムを作る場合、一度作って終わりというわけではなく2、永遠に自分達でメンテナスをしていかないといけない。そういうことも念頭において「未来の自分達のため」にも正しく作ることを意識しよう。
「丁寧に正しく作る」例
丁寧に正しく作る、というのは、基本の繰り返しでしかない。例えば、
- RDBを使うなら、
- トランザクションが必要かどうかきっちり判断したか
- ロックする範囲は最小になってるか。デッドロックは起きないか?
- 正規化、非正規化についてしっかり考えたか
- 富豪的になりすぎてないか
ハードウェアは確かに速くなって安くなった。でもあらゆることをを富豪的に作っても吸収してくれるほどではない。 - WebページのHTTPヘッダーは適切にセットされてるか
- キャッシュ(しない?ブラウザで?proxyで?)のことは考えたか
- ログファイルのローテーションは正しくしてるか
- ログファイルの監視はできているか。ただのディスクの肥やしになっていないか
- テストは正しく書けているか
- テストは正しく常に実行されているか
- サーバの監視や異常の際の通知はできてるか
- エラー処理が正しく出来ているか
- 人間が同じ作業を繰り返さないでいいような自動化ができているか
- ドキュメントは必要な粒度でしっかり残っているか。更新されているか
- 無駄なドキュメントを作り過ぎてないか
などなど
一つ一つは「わかってる!」ということでも、つい手を抜いてしまうことも多いので、そこをさぼらないということをチームとしてできることを考える。
2. 丁寧に正しく作れないなら既存のプロダクトを使う
丁寧に正しく作るのにはしっかりとしたリソースが必要。ただし、人やお金というリソースは常に限られているので自分たちが集中しないといけないところをしっかり決めよう。
独自フレームワークはやめよう
独自のフレームワークは正しく作れないならやめよう。「正しく作る」というのはメンテナンスとか含めて。もし作るなら専用のリソースを確保するぐらいできないと無理。片手間でフレームワーク作りとか無理。外部に向けて公開しても恥ずかしくないレベルのものを作れるようでない限りはやめたほうがいい。
独自なスケーラビリティやパフォーマンス向上は最終手段
分散やキャッシュなど、プログラマなら誰もが自分ならもっといいものが書ける、書きたい、となってしまいがちだけど、これもフレームワークと同じでできるだけ既存のプロダクトで行くほうがいい。この辺は本当に難しいので、簡単に手を出してはだめ。
もう一つの問題点
作るのが難しい、メンテナンスが難しい、ということに加えて、もう一つの問題点は、新しく加わった人の教育コストが高くなるということがある。まず、外の資源(ドキュメント/本/Google検索)が使えないので自前ですべて教育をしなくてはならない。また、独自であるが故に、そのフレームワーク等の経験者を採用することもできない。新しく入った人は常に0からのスタートになる。これは立ち上がりのスピードを重視する環境では難しい。
また、上述したようにリソースが足りなかったりで、その独自に作ったシステムがいまいちの場合、そのシステムに対する新しく入った人のリスペクトが得られないことにも繋がり、その結果その人のモチベーションの低下にもつながる。
この辺りをすべて背負ってもでも、本当に自分たちで作ったほうがいいのか考えよう。
3. システムのモジュール化/疎結合を考える
自社で自社システムを開発していると、わかりやすい「開発の切れ目」のようなものが、なかなかないので、どうしても「拡張、拡張」になってしまい、全体的に見ると継ぎ接ぎのシステムになってしまう。その結果、すべてが一枚のモノリシックなシステムになってしまい、部分的に新しいことを試したりするのが難しくなってくる。
具体的には、8年の歴史の内、5年くらいは、ほぼPHPとFlashで作ってきて、それを2年ほど前から、全面的にRuby on RailsやJavascript/CSSといったものに移行している。正直なところ、結構辛い。サブシステム毎に、別システムにしておいて、それぞれを疎結合(APIとか/DBレベルで結合)するようにおけばよかったかなー、と思っている。そうすれば、部分的に新しい技術で置き換えるのはもう少し簡単にできたのかもしれない。
もちろん、そういう作りにするとサブシステムそれぞれに、同じようなものをポーティングする必要が出てきたりとオーバーヘッドも大きくなるので、その辺はよく考える必要があるのだけど。
4. 技術的負債はできるだけ早く返そう
できるだけ丁寧に作っても、どうしても 技術的負債 はたまっていく。会社が少しでも落ち着いたら負債の返済について考えよう。
返済を後回しにすると、負債の利子が貯まるばかり。利子はあちこちで発生する。「運用で回避」のコストで払ったり、顧客対応に取られる時間で払ったり、システムに対する信頼の低下ということで払ったりもする。また、緊急の徹夜作業で払ったり、と言った人に負担をかけることで払うこともあるだろう。
うちでは、少し前から返済に当てる時間を多くとっているのだけど、今思うと数年前にも大きく返済できるタイミングがあったはず。でも、それをやらずに走り続けてしまった。その結果、利子をずいぶん払ってしまった気がする。
技術的負債を負いつつ、でも走り続け経営がそれなりに安定する。このときに、最低一回は技術的負債の棚卸が必要だろうと思う。全部返済できなくても、利子の高いもからどんどん返済していくべきだろう。
どうしても、一度落ち着くと、さらに次へと攻めたくなってしまうのだけど、そこで一度振り返ることがとても重要だと今は思っている。
サーバ周りの反省点
最初に書いたように、データセンタ内に自社でサーバを運用もしている。これにも色々な反省点がある。
5. ハードウェアは正しく適正なものを買おう
初めの数年間、知識的なことや時間が足りなかったもあり、サーバを購入してそのまま使って、遅くなったらあまり考えずに新しいサーバを追加していた。今思うともったいないことをしていた。「システムが遅い」となったら、何が「遅い」のかを調べ、それに対する効果の高い投資をしよう。
それと、自分自身、根がケチな上にソフトウェア側の人間なので「そんな高いマシン買わなくていいよ。遅かったらソフトウェア側で頑張るよ」とか思いがちだったのだけどそれは間違い。ソフトウェアでカバーできないこともたくさんある。ケチって中途半端なサーバを買ってしまい、拡張性(メモリやディスクの増設等)が低く、にっちもさっちもいかなくなる、仕方ないのでまた買う、という悪循環に陥る。
もちろん、割り切って安いサーバをたくさん買ったりするのは別の話だが。
6. サーバ周りの人材をしっかり確保しよう
上のことをきっちりやるためには、最初から、少なくとも、お客さんが少しでも着いた後は、専任の人を入れたほうがいい。
最初のうちは、サーバ周りもプログラマ中心にやっていたのだけど、やはりそれには限界がある。本職の人がやるのは、プログラマが片手間でやるのと比べ、深さも広さも全然違う。
また、プログラマはシステム開発に集中した方がいい。
7. クラウドも検討しよう
8年前には、選択肢や自由度がなかったのでどうにもならなかったけど、現在では、場合によってもクラウドも検討すべきだろう。もちろんリスクやコストを正しく判断する必要あるけど。
組織についての反省点
人が大事。チームワークが大事。本当に大事。ちょっとシステム開発からは離れてしまう部分もあるけど、とても関係するところなので。
8. いい文化を作ろう
これは本当に難しい。自分の中で全然答えが見えない。
人々の行動が文化を作っているような気もするし、文化が人を作っているような気もする。まあ、好循環が大事なのだろう。先にいる人やチームの行動や習慣は、いい意味でも悪い意味でも新しい人に伝播してしまうので、そこは常に意識すること。成功している企業にはかならずいい文化があるものだろう。
一度できた文化は、いい文化も悪い文化も継続しやすいことを常に頭に入れる。
9. 途中から入ってきた人
こういう小さい会社に途中から入ってくる人というのは「今、会社が直面する課題や問題」を解決するために入ってくることが多い。ミッションが明確なだけに、そのミッションを完遂することがその人のプライオリティになるし、その人ももちろんそのつもりで入ってくる。
もちろん、会社も周りもそれを一番期待している。しかし、期待しているのだけど、組織で動いていると、そのミッションと前からいた人のミッションがデッドロックすることもある。また、会社のリソースは有限なので、その新しく入った人が完遂するのに必要なリソースを必ずしも割り与えられるとは限らない。
そういう事態になってしまうと、入ってきた人が腐りがちになってしまう。最初からいる人はその辺の事情を汲みとって、現在できる範囲で実現する方法を探すのだけど、途中から入ってきた人はその方向になるのはとても難しい。この辺のサポートがすごく大事。
10. 朝礼? 日報? 週報? 社内勉強会?
この手の物には否定的だったのだけど、やる意味が多少はあるのかな、と最近は少し思っている。初期の頃はそういうのがなくても情報は共有されるし、士気も高いんだけど、人が増えてくるとどうしても必要になってくるような気がしている。
この辺は、完全に否定せず、必要な物はどんどん取り入れていきたい。
11. 社内の距離感 / 縦割り
「途中から入ってきた人」にも関係することなんだけど、最初のうちは全員が同じ方向を見ているのだが、だんだん社員が増え大きくなるに従って、社内で距離感が出てくる。部署毎の損得、面子、プライド。みたいのが出てくる。「こんな小さな会社でそんなこと発生するのがバカバカしい」とか思ってしまうのだけど、人が増えるとどうしても発生してくるようだ。
特にうちの場合、オフィスがロンドン(開発)、日本(営業/運用)、中国(営業/運用)と分散しているので、元々物理的な距離感がある上に、だんだんと精神的な距離感も出てきてしまった。
そして、物理的/精神的に離れていると、ネガティブなことはそれでも伝わるのだけど、ポジティブなことは伝わりにくくなっていく。たとえば「この機能かっこいいね」とか「この前のやつ結構お客さんの評判いいよ」とか、日頃のちょっとしたポジティブの感情がとても伝わりづらい。特にうちは非ITな感じの人も多いので、そうなるとなおさら。そうすると結果的に「いつもあいつらは文句言ってる」ということになってしまう。そうしていると、「共通の目的/ゴール」を持つのが難しくなっていく。
この辺は早いうちに手を打っておいたほうがいい。もちろん、全員が一箇所でできる方法があるならそれがベスト。
12. 採用について
こういう小さな企業に入ってくれる優秀な人材はなかなかいない。それでも、最初の小さ過ぎる段階では、勇敢な(無謀な?)チャレンジャーが集まる。しかし、ある程度軌道に乗ってしまうと、外から見ると「ただの中小企業」になってしまい、だんだんと難しくなっていくのを強く感じた。
基本的に、スタートアップ企業にできる人材確保の路線は2つしかないだろう。一つは既にいる社員の紹介、もう一つは技術者が入って楽しい企業ということを外にアピールすること。うちの場合は前者でそれなりに回ってきているのだけど、後者がほとんどできていなかった。
また、その上で、常に数カ月先を見越して採用活動をしておくことも大事。人は探してすぐ見つかるものではない。
最後に
上のことは今現在思うこと。
ただ、ここまでずらずら書いておいてあれなんだけど、これらのことは全部、条件次第でいくらでも変わる。上のはこの8年間で与えられた条件の中の行動に対しての反省点。今後全く同じ状況が現れることは、結局はその場その場で考えぬいて進んでいくしかないのかな、とも思ったりもする。
そして、それがスタートアップ企業の楽しみ方でもあるのかな。
1 ちなみに、次の挑戦の具体的な予定はないし、というか、まだ今の会社で挑戦中! でも、正直なところ、もう一度何も無いところから自分の力を試してみたいという願望がないことはない。まあ「すべてをやり直したい症候群」はありがちなので、これもそれかなと思ってもう少し今の会社でまだ何ができるか、というのを考え実践するつもり。
2 SIer的な開発が、作って終わりと言ってるわけではないですが。
何を今更、なことかもしれないないのだけど、もしかしたらこれを知ることでTDD(Test-driven development)をやることのハードルが一気に下がる人がいるかな、と思ってメモ。 特に、ある程度プログラマとして経験があるけど、どうもTDDは慣れないという人向き。
“TDDとは、TDD以前に脳内や機上でやっていたことをコードに落とすことに過ぎない”
このことが解ってから、TDDをするのが一気に苦痛ではなくなり、むしろ楽しくなった。
TDDでなくても、コーディングをするとき、temporaryなテストコードを書いたり、目視でのチェックはしたりするものだろう。たとえば、一時的に変数の値をハードコードして挙動を変えてみて、それを目視で確認したり、printデバッグとかもその一部だ。
つまり、このtemporaryなコードや目視している部分をpermanentにするのがTDDで書くテストコードだということがわかった。
TDDであろうが、なかろうが、考えないといけないこと、やらないといけないことはたいして変わらない。たいして変わらないなら、テストコード書いておいた方が得。
テストコードがあれば、チーム内で技術や知識の共有もテストコードを通じてしやすくなるし、コードレビューだって、コードとテストがセットの方がやりやすいだとやりやすい。
単純に、テストファーストでやらないのは色々と損だよね。って感じ。
後は、おまけみたいなものなんだけど、自分でTDDしたり、人のTDDでしたコードを見たりして、いくつか思ったことをメモ。
1. TDDでコードや設計がきれいになるのは、プログラマをさぼらせないから
いいコードでないとテストが書きにいので、「いいコードを書くのをさぼれない」ってことになって、結果的にきれいなコードにはなりやすい。人はさぼるものなので、これ重要。
2. TDDのUnit Testは自作自演
TDDでは、テストコードを書く人と実際のコードを書く人は通常同じなので、どうしても自分の都合のよいテストコードを書いてしまいがち。また、仕様を誤解してても、その誤解したままのテストコードになって、テストは通る。もちろん、自作自演だからこそ、1.の恩恵を得れる。
3. ある程度の範囲に影響するリファクタリング時の動作の保証は微妙
Unit Testは、どちらかというとホワイトボックス的なテストなので、リファクタリング時にもあわせて変えないといけないことも多い。そのため、ある程度の範囲を直す場合、Unit Testだけでは不十分。動作の保証という意味ではブラックボックス的なcucumberみたいなテストも組み合わせないと厳しい。(もちろん自動化以外のテストも必要だけど、これは別の話)
これはTDDってよりUnit Testの話か。
4. テスト自体の知識も大切
当然なんだけど、TDDをするようなツールの知識だけではなくて、ある程度のテストの知識がないと良いテストは書けない=良いTDDはできない。テストの知識ってのはCOBOLの時代(もっと前かな)から変わってないような知識。境界値チェックとか(ちなみに、こういうの勉強するのに情報処理試験の勉強ってなかなか侮れない)。
単純な例を挙げると、ある条件での平均値を求める、と言ったコードに対するテストで、テストコードで使うテストデータがその「ある条件」のものしか用意してなく、「ある条件」以外のものを用意してないといったケース。この場合実際のコードの方に「ある条件」が抜けていてもテストは通ってしまう。超基本なんだけど、それすらできてないテストを見かける。
単体テスト仕様書を何百枚も手書きしていた世代のノウハウをゲットすべき(関係ないけど、Key-Value Storeな方面でもあの世代のノウハウって役に立ちそう)。
ちょっと逸れるけども、知識どうこうではなくて、手を抜いてしまっているダメなテストももちろんダメ。
たとえば、Rubyとrspecでの例になってしまうが、 foo.should_not be_nil を安易に使いすぎるとか。foo.should == something とちゃんと書けるようなケースなのに、should_not be_nil で手を抜く。
他にも、Mock/Stubを使いすぎて、何もテストしてない状態になっている、とか。笑い話みたいだけどたまにある。
こういうのは「自分が何をテストしているのか」というのをしっかり意識しないと陥りがち。
そういえば、「TDDは品質のためじゃない!」 と、でかい声で言ってる人でも、多分このぐらいのことは当然やってる人たちなんだろうけど、なんか誤解は与えそうだね。とは思った。
まず自分のFlashに対する経験を書いてみる。
Flashとバックエンド(サーバサイド)の間に立ってコミュニケーションをするモジュールをFlexで作った。というのが一番技術的にFlashに近づいたところかな。
これを作る前はFlashが直接バックエンド(またはJavascript経由でバックエンド)と直接コミュニケーションしていたんだけど、色々と問題(実装したFlash毎に通信方法が違ったり、通信環境のエラー処理をFlash側にそれぞれ書かなかったりいけなかったり)が出てきたので間にデータハンドリング用の層を作った。また、Flashから直接サーバサイドやJavascriptとやりとりするのを禁止にして、Flashのプログラマの人がpureなFlashの制作に集中できるようにした。
それと、ActionScriptというところで言うとAdobe Flexでそれなりの規模のアプリケーションは作ったことはある。
そんな感じなので、ActionScriptはそれなりにわかるけど、Flash固有のこと(タイムラインとか)はよくわかってないと言った感じ。でも、なんとなくFlashがどう動いているかというのはわかっているつもり。それと、それなりの数のFlashのプログラマと一緒に働いてきて、Flash開発とFlashの開発者の人の傾向みたいなものもなんとなくわかっているつもり。
で、自分が感じている開発視点でのFlashの強みは、
- 生産性は高い? これは正直微妙なところもある。ただ、HTML+Javascript+CSSが好き(でFlashが嫌い)な人は「それ、HTMLでもできるよ」ってすぐ言うのだけど(エフェクトとかね)、やっぱり生産性、保守性という面ではまだまだFlashには敵わないかな、とは思っている。もちろんFlashが得意なところに限る。テキスト処理みたいな苦手なところで使うと一気に生産性ダウン。
- 人材を確保しやすい。これも微妙か。数年前は、html+Javascript+cssで、きれいに動きを付けられる人を探すのは、Flashな人を探すより大変だった。最近はそうでもないのかなー。
- オーディオやビデオの再生や録画と言ったところはまだまだFlashでしか(簡単に)できないことも多い。
- ブラウザ間の互換性を(あまり)気にしなくていい。
弱いところは、
- テスト系のツールが遅れてる。Unit Testがあるぐらい? 画面系の自動テストも、商用含めいくつかあるみたいだけど、あまり標準的なものもなく、結局未だに手でテストしてる。TDDだBDDだ言って盛り上げっているところから見ると、あれ?って感じ。
- SCMが使いづらい。Flashだとまだバイナリファイルで管理している部分(fla)があって、SCMでの管理が非常にしづらい。Flexならほぼ全部テキストベースのコードで行けるのだけどね。このご時世に「いつの間にかファイルが先祖返りしてた」とかありえない。
- バックエンドのことまで考えられる技術者が少ない? これはもう半分諦めて、インタフェースの部分はバックエンドの技術者が作ってしまった方がよさそう。
- テキスト系が弱すぎる。Flashというより、Flash Playerの問題か。この時代に、もっさり感のあるテキスト入力とかちょっと考えられない。
- ブラウザ間の互換性はあまり気にしなくていいけど、Flash Playerのバージョン間の互換性は知っておく必要がある。
うーん、「みんな悪く言うけど、Flashそれなりにいいもんだよ!」と思って書き始めたんだけど、こうやって書き出すと悪いところが目立つようになってしまった。あらら。
今後は、Flashが強いところをピンポイントでモジュールとして作り、htmlとJavascriptの中でそれを調和させつつうまく使う、というのが正解なんだろうな。Adobeの中の人も同じようなこと言ってたような気がするけど。で、その辺のいい感じのフレームワークみたいのがあればいいのにね。
そうなってくると、Flash技術者の人はhtmlとかJavascript/CSSの知識も持っていないとツラそう。いかにhtmlと協調して、違和感なく動くようなFlashモジュールを作れるか、というのがポイントになってくるので。
なんとなく、メモっておきました。
これまではcommitされたコードを、commit(push時)メールでなんとなく見ていたが、取りこぼしも多いし、忙しいと、つい見なくなってしまうので、なんかいい方法はないかなとここ数カ月くらいぼんやり考えていた。で、簡単なスクリプトでできそうと気づいたのでメモ。Githubに置いてあるようなオープンソースなコードとかも追いやすいんじゃないかなー。
ちなみに、このスクリプトを書く前に、コードレビューシステム的なのを導入しようかとGerritとか、Review Boardを少し試してはみた。でも、うちで使うにはちょっと大げさ過ぎるので、導入してもツールに踊らされる or 使わなくなる、という感じがしたので、とりあえずやめた。
いまいち気に入らない点としては、Gerritとかは完全にcommit単位でのレビューなんで、ちょっとしたパッチレベルならいいのだが、がりがり書いていく中ではちょっと現実的ではないかな、という感じがした。同じ機能追加/修正のために、同じ日に同一のファイルの同じような場所に複数回commitする、ということはよくあると思うが、そういう場合に1 commitずつ見る意味はあまりないかな、という感じ。それと、普段エディタで書いてるので、レビューもエディタでしたい、ってのもある。
追記: 指摘されたのだが、もちろんGitのsquash機能を使いこなせば、きれいな1 commitにまとめられることも多いだろう。でも、それでもやはり限界はあるし、綺麗なcommitセットとログを作ることに時間をかけるのも本末転倒な部分もあるかな、と。もちろん 1 commit は 1機能の追加/修正するという原則は基本的には徹底したいんだけど。それと、個人的には複数人が同じようなところ修正した場合も一個の差分として見た方がわかりやすい。
自分の中でやりたいことをまとめると、
- 毎日レビューするという前提 > そこまで大量なコードを相手にするわけではない
- リポジトリの(あるブランチ)中全部のコードを対象にしたい
- 前回レビューしたところから現在までのソースの差分を見たい
- 同一のファイルに対する複数回のcommitは一個の差分として表示
- ただしcommitログはファイル毎に全部表示
- レビューで問題や疑問があった場合には普段使っているBTS(Redmine)に投げる (ここはスクリプト化しない)
- 毎日のことなので、上記のことをコマンド一発で簡単にやりたい
こんな感じのスクリプトを作った。ほとんどGitのコマンド並べただけだけど、短いので全文出しとこ。
#!/bin/sh
if [ ! -d ".git" ]; then
echo "run in the git top directory."
exit 1
fi
reviewed_repo="REPOSITORY_TO_STORE_REVIEWD_CODE"
reviewed_branch="BRANCH_FOR_REVIEWED_CODE"
reviewed_combined="${reviewed_repo}/${reviewed_branch}"
output_file="reviewing.patch"
rm -f ${output_file}
for file in `git diff --name-only ${reviewed_combined}`; do
echo "==================================================================" >> ${output_file}
git log ${reviewed_combined}.. -- $file >> ${output_file}
echo "" >> ${output_file}
git diff ${reviewed_combined}.. -- $file >> ${output_file}
echo "" >> ${output_file}
done
if [ -f ${output_file} ]; then
emacs -nw ${output_file}
read -p "Approved?(y) " ans
if [ "${ans}" == 'y' ]; then
git push ${reviewed_repo} HEAD:${reviewed_branch}
else
echo 'ignored'
fi
else
echo "No unreviewed commit."
exit 1
fi
処理の流れ: (1-2 をするようなGitコマンドがありそうだが、探せなかった)
- git diff --name-only で前回から変更があったファイルの一覧取得
- 変更があったファイル毎に、git logとdiffで自分が欲しい情報作成
- 作成した差分をエディタで開く
- エディタを終了したら、プロンプトを出して、reviewが終わったか聞く
- 終わった場合には、現在までのcommit情報をレビュー済branchにpush
「前回レビューしたところまで」という情報をGitにブランチとしておくことで、未読管理とかめんどくさいことを全部gitにお任せできるので楽。これを置くところはローカルでもリモートでもいいんだけど、今回は未読情報を一箇所に置いておきたのでリモートに置いている。
たったこれだけなんだけど、なかなか気に入っていて、一日の中で時間が空いたときに、reviewしたいリポジトリに行って、 rv (このスクリプトに付けてる名前) とタイプするだけで、前回レビュー時からの差分が、慣れている emacs に出てきて、レビューが開始できる。オリジナルのソースにジャンプしたい場合とかもemacsのdiffモードにお任せなので楽々。
前もってpullさえしてあれば、オフラインでも当然動くので(最後のpushは今はリモートのリポジトリにしてるのでそこだけは場合によってネットいるけど)、電車で移動中とかどこでもできるのもいい。
たいしたことではないけど、日常なことを簡単にできるようにするってのは効率化(=自分の本当にやりたいことの時間を増やす)ための一歩ですよね。
しばらくこれで行ってみて、きつくなったら、レビューシステムを再検討してみるつもり。
追記: はてブでわかりにくいという反応を頂いたので図を書いてみた。数字は上のリストの数字と対応してる。正確にはReviwed branchはローカルに持ってきてからlogとdiffをする感じ。
全体的に、なんかもっといいGit標準的なやり方がありそうで、ちょっと自信ないのだけど。
「The Rspec Book: Behaviour Driven Development With Rspec, Cucumber, and Friends」という本。
この本は、RspecとCucumberを使い、どう考え、どうシステムを作っていくか、というをチュートリアルを交えながら紹介する構成になっている。
ただUnit Testを紹介するだけではなく、Unit TestツールであるRspecに、BDDツールであるCucumberを組み合わせて使うことで、Unit Testでカバーできない部分をCucumberで補い開発をする、というところがこの本の肝になっている。
この本を読み、実践することで、Unit Test*だけ*を書いてシステムを作っているときのモヤモヤ感をかなり解消できる。また、RspecもCucumberも知っているけど、両方をどうやって使っていくかよくわからない、という人もこの本を読むとかなりすっきりできるのでお勧め。
以下に、自分なりに理解したところを書いておく。本の内容から少し離れているところもあるので、詳しくは本を読んでみて欲しい。
また、ここでは触れないが、この本では、RspecとCucumberの基本的な使い方や、webratやseleniumなどのWeb系のテストツールと一緒に使う方法や、Railsとの組み合わせ方なども含まれているので、Rubyでのテストに関するかなりの部分がカバーされていてかなりお得な作りになっている。
Unit Testとは
改めて書くまでもなく、Unit Testというのは(自動化するかどうかは関係なく)、とにかくコンポーネントとしての精度を高めるものである。また、Unit Testの優れている点として、Unit Testは「テスト」だけのためではなく、「設計」のためであるということが言える。テストを書くということ自体が設計となる。テストが書きにくい=設計が悪い、ということでもあるので、テストをきれいに書くことで、自然といい設計になるように導いてくれる。
しかし、Unit Testは、コンポーネント単位での動作は保証するが、それらを組み合わせたときに、最終的にシステムがどのような物になるか、ということまでは基本的保証しない。そして、Unit Testは、基本的にプログラマの自作自演ということもあり、外部から、システムとして見たときにどう動くかということも保証しない。また、非エンジニアの人にどういうシステムか、というのを説明するのにもUnit Testは向いていない。
これらは別にUnit Testの欠点というわけではなくて、Unit Testはそういうものなのである。
「Unit Testだけ」で開発するモヤモヤ
Unit Testでカバーできる範囲は「設計」を設計としてそのままコードに落とし、そしてそれがそのままテストにもなり、自動化もでき、再利用もできる。
しかし、Unit Testの対象外である、システムを外部から見たときの振る舞いに関して、設計やテストはコード化されずに、BTSや色々なツールで管理しても、結局「使い捨て」になりやすい。ドキュメントをコードとは別に書いても、プロジェクトが進むごとにコードとどんどん乖離していってしまう。
今まで、この部分を解消するためにいろいろなツールやプラクティスを試してみたが、どうも自分的にしっくりくるのがなかった。それは、おそらく、そういうものの多くは、ドキュメント(紙)ベースなものが多いからだと思っている。やはりプログラマとしては、そこも「コード」で定義したいし、コードに書いてあることをドキュメントにも書くということはしたくない。
Cucumberとは
このモヤモヤを解決してくるのがCucumberというツールと、それを使ったプラクティスになる。Cucumberでは、外側の設計(とテスト)をコードに落とすものである。Cucumberを使うと、ユーザから見た動作に関しての設計を、そのままコードにすることができる。Unit Testでコンポーネントに対してテストを書くのと同じ構図になる。
また、Cucumberは非常に自然言語に近い形で書けるので、非エンジニアの人が読むことが(場合によっては書くことも)可能だ。
RspecとCucumberの連携
RspecとCucumberは、それぞれ単体で使っても、もちろん便利なのだが、この本では、それらを組み合わせることで、さらに強固で柔軟なシステムを作ることができると説明している。
コンポーネントレベルでの動作の保証にはRspecを使い、ユーザから見た動作に関しての保証にはCucumberを使うということになる。
この本では、1つの機能毎に下記の各フェーズを繰り返し開発を進めて行くことを提案している。
- Cucumberを使い外部の設計(=テスト)を書く - この段階では実装がないのでCucumberに実行結果はRed(失敗)になる。
- Rspecを使い内部の設計(=テスト)をする - この段階では実装がないのでRspecを実行するとRedになる。
- Rspecが通るように実装する。この時点でRspecはGreen(成功)になる。
- Cucumberが通るようにCucumberの実装を書く。ここで、2-3で作った部分がうまく1.の設計にあわない場合、2.に戻る必要がある。場合によっては1.に戻る。
つまり、外側 (Red) - 内側 (Red) - 内側 (Green) - 外側 (Green) という順序で開発して行くことになる。
この手法は、非常にアジャイル開発とも相性がいい。上記の1から4の一回で作る機能の単位として、アジャイルの一つのユーザストーリはぴったり当てはまる。
本の入手方法
ここまで書いておいてなんなんだが、まだこの本は紙の本としては発売されてない。延期につぐ延期で、次の予定は2010/4になっている。しかしとりあえず現時点ではベータ版をPDFで買うことができる。上のリンクから買える。
一度購入しておくと、新しいベータ版が出る度にダウンロードできるので、個人的にはここ半年位楽しんでいる。英語だが、チュートリアル形式でコードも多いので英語にあまり自信がなくてもなんとかなるだろう。
Amazon日本でも紙の本の予約はできるみたい。