2012-12-04

中間地点としての PySpa - PySpaアドベントカレンダー4日目

このエントリは、PySpaアドベントカレンダー という「思いの丈を綴る(なんでも良い)」ブログイベント4日目の記事です。比較的歳をとってから、プログラマになろうとしたおっさんの吐露です。


能書き


PySpa とは「Python温泉」を語源とするはずで、開発合宿と見せかけて、なんとなくソフトウェア開発者やその周辺の人たちが、温泉旅館を借りきる集まりのひとつ、と理解しています。

アドベントカレンダー栄えある1日目は @akisutesama が書いているわけですが、いきなり事実誤認があるわけですよ。

例えば今回のアドベントカレンダー参加メンバーだと 
12/1 akisutesama
12/3 tokibito
12/4 torufurukawa
12/8 shimizukawa
あたりがPySpa経由で雇用されたビープラウド社員だったりします。

待てやこら。PySpa に初参加したときには、すでにビープラウドを退職した後ですよ。

とはいえ、私のソフトウェア開発者としてのキャリア形成で、PySpa は重要な要素なのです。さて、このアドベントカレンダー、なんでも良いって説明なので、俺のPythonとPySpaの話をしちゃうぞ♡  ハウツーとしての意味はありませんが、考古学的価値はあるかも知れません。


いまはむかし…


34歳ごろ、計測器メーカーでマーケティングの仕事をしつつ、2ちゃんねるの「35歳なんだけどプログラマになれる?」みたいなスレを見ていました。学生の頃からプログラマになりたかったのですが、面接に落ちたり、大学院を中退したり、面接に落ちたり、面接に落ちたり、女の子に振られたりしているうちに、負けることに慣れきってしまい、いろんなことを諦めていました。その割には、でもやっぱりプログラマやりたなぁ、などとぐずぐず2ちゃんねるですよ。

どういうきっかけがあったのか、まったく覚えていませんが「転職して職業としてプログラマになろう!」と決めたような気がします。どうせキャバ嬢にそそのかされたとか、そんなことがきっかけでしょう。で、面接とか受け始めるのですが、まあ、30代未経験が受かるわけがありません。

そういうとき、ググると、自分に都合のいい解釈ができるウェブサイトがすぐに見つかります。そのひとつがアクセンスのおまけ というページです。たぶん、ここに書かれていたような気がするのですが、当時「Python ハッカーを募集」って書いてあったのです。いまはもう書かれていません。ほら、世界は俺を求めている、とか思って安心だけしていました。いかにも不採用になりそうだったので、応募していません。

ハッカーに会おう


ひっぱってすみません、もうすぐ PySpa 出てきます。

普通に自分の職歴で転職しようとすると、どうしても、計測制御分野でマーケティング担当とか、マネージャとかになってしまうんですよね。ちがうんだよ。プログラマになりたいんだよ。ってなわけで、きっとハッカーたちが居るであろう勉強会に顔を出そうと思いました。初対面で話すとかまったくできないので、話聞いて帰るだけでした。行ってたのは Python Code Reading という勉強会で、そこで Python 3.0 の紹介をやらせてもらえることになりました。

講師をやると、その後の懇親会で、何もしなくても話しかけてもらえます。これすごく楽なんですよね。そのとき @atusi さんにも話しかけてもらえました。

atusi「いつも、どんなとこ行ってるんですか?」
私「いえ、ここ (Python Code Reading) しか来てないんです」
atusi「Python やってるんだったら、 BPStudy と Python 温泉っていうのに行ってみたらいいですよ」

おまたせしました、やっと登場、Python 温泉 a.k.a PySpa です。

atusi「Python 温泉は、集まってコード書いたりだらだらやってるだけなんですけどね。」

おお、ハッカーと温泉で仲良くなって、会社に入れてもらおう!という甘い策略を思いつきました。

私「ありがとうございます。早速申し込んでみます」


しかし…


当時の PySpa は、開催の告知をしたら、一瞬にして枠が埋まってしまって、ぜんぜん参加できなかったのです。くぅぅぅぅ。

けど真っ向勝負をしていたら、いつまでたってもプログラマになれないわけですから、PySpa に参加し、アクセンスの人と仲良くなり、うっかり間違って採用される、という目論見を持ち続けていました。

一方で、ソフトウェア開発の勉強会は、転職とは関係なく面白かったので、Python Code Reading や BP Study には、ときどき参加するようにしていました。あ、いや、ちょっとは期待があったかも。すみません、すみません。その後、ビープラウドで募集があったときに拾ってもらい、めでたくプログラマになれたわけです。


で、PySpa は?


その後も PySpa は開催されていたのですが、仕事や個人の都合、あるいは震災があったりとかで、参加していませんでした。やっと参加したのは、今年になってからです。

PySpa 経由で転職、というのは、まったく見当違いな目論見だったわけですが、 PySpa という道標があったから、その界隈にうろうろすることになり、プログラマになれました。

いまどき、35歳でプログラマになりたいような人がいるのか分かりませんし、PySpa を目指してもおそらくプログラマになれる気はしませんが。これを読んで PySpa 目指すよりも、手を動かしたほうがいい気がします。なので、この文章から教訓や方法論は得られないでしょう。最初に書いたように、おっさんの吐露です。

けど、まあ、何か偉そうに書いちゃおう。思いの丈を綴っていいらしいし。はっきり言って、プログラマになって後悔したことなんて、何回もあります。これからもあるでしょう。マーケやってたほうがモテたかも知れません。けど、プログラマになっていなかったら、もっと大きな後悔をしたはずです。死ぬまでずーーーーーっと「私は本当はプログラマになりたかったのに」と悔い続けたであろうことは、想像に難くありません。行動の諦めはいいくせに、気持ちだけ未練がましい私が、なんとか蟻地獄から抜け出せたのは、PySpa をゆるいながらも目指し続けていたからです。

ちなみに PySpa は、当初想像していたものとは、ぜんぜん違いました。


明日は、Python と人生とお酒の先輩である @turky です。

2012-11-24

Max Kanat-Alexander / コード・シンプリシティ


Max Kanat-Alexander の「コード・シンプリシティ」を読みました。「Bugzillaプロジェクトの主任設計者の実体験に基づいた、ソフトウェアの簡潔性を保つさまざまな知見をまとめた書籍。」と紹介されています。

ブログを元にしているのか、Joel on Software やハッカーと画家の、もうちょいゆるい版と思うのがいいと思う。また経営者的な目線ではなくて、現場のマネージャやーリーダーの目線で書かれているという印象です。

悪いプログラマーと良いプログラマーの違いは理解力だ

プログラマの良し悪しに関する定義は、人の数だけありそうですが、ここでは理解力で。

ビジネスバリュー


ルールや指南が有効であるための前提というのが存在して、それを読み取れるか、というのは重要だと思う。この本では理解力を求めるけれど、あんまり前提に関して明確に書かれていない。だから読み取らないといけない。

ソフトウェアは人を助けるためにある

「金儲けをすること」は、個人的な目的、または組織の目的には当然なり得る。金儲けをすること自体に何ら問題はないからだ。ただ、ソフトウェアの目的にはならないだけだ。売り上げとして入る金額は、作ったソフトウェアがどれだけ人の助けとなっているか、ということと直接つながっているはずだ。


究極的には誰かが恩恵を受けていて、そのためにソフトウェアは存在する、という考え方。そうなっていなければ、そのソフトウェアには価値がない、というポジションです。特に著者はオープンソースソフトウェア開発の経験から語っているので、金銭的な利益だけに還元せずに、価値を計る方法が必要だったのだろうと勝手に邪推しています。

ソフトウェアの価値と、その大きさという指標はすごく大事だと思います。「この機能を追加すると、あるいは、この作業をすると、どんな価値が生まれるのだろう」と考えること多々あり。まだビジネス的な価値評価や、バリューチェーンが分かっていないので、常に分かるわけではないけれど、そういう視点で見るようにしています。

「こういう実装にするのが良いから」「こういう技術を使うといいから」みたいな動機だけでやらないように注意しています。ある実装方法がださださであっても、費用対効果が著しく高いのであれば、そうすべきだと考えているからです。

変更に備える

未来についてまったく予言せずに、いますぐにわかる現在の情報を元に、すべてのデザイン上の決定を行うのが最も安全だ。

これは、本論で論じてきた内容とは真逆に聞こえるかもしれないが、そうではない。デザイン上の決断を行う際、最も重要なポイントは未来だ。しかし、将来、変更ができるようにデザインを行うことと、未来を予言しようとすることは、違うことだ。


変更についていろいろ。変更しやすいけれど、特定の予想シナリオに依存しない、というのがどういうことか、また、一般的なやりかたがあるのか分かりません。分かりませんが、なかなか勇気づけられます。

最近、私が気にしているのは変更に対応しやすいコードです。要件はこの日までに、とか言ってもどうせ決まらないという諦めがあります。諦めの姿勢の是非は、今は問わないで。決められない人や、経済的な力関係で無茶してくることがあります。また、パフォーマンスのチューニングをしている過程で、やべぇこれじゃだめだ、って発見することも残念ながらあります。

そんなときに変更しやすくしておかないと泣きます。そして、最初に作っている時点では、どんな変更になるかは分かりません。

将来、要件が別に出てくる可能性を排除せずに、直近で既知の要件に基づいたデザインが必要だ。システムがしなければならないことが「X」のみであれば、いますぐその「X」ができるようにデザインしよう。未来には「X」でないことがあるかもしれないから、それを念頭に置いておけばよい。現時点でシステムは「X」だけができればよい。

古い考え方かも知れませんが、私が注意をするのは、低い結合度と高い凝集度を目指す、というものです。いきなり抽象クラスを作ったり、フレームワーク化をしません。ただ、作っているものや技術によると思います。


加減乗除ができる電卓プログラムにこの手法を使った場合、次のようになる。
  1. 足し算だけを行うシステムを計画する。
  2. 上記のシステムを実装する。
  3. 引き算機能を追加しやすいように作ったシステムのデザインを修正する。
  4. 引き算機能をシステムに実装する。足し算と引き算しかできないシステムが出来た。
  5. かけ算機能を追加しやすいようにシステムのデザインをさらに修正する。
  6. かけ算機能をシステムに実装する。足し算、引き算、かけ算だけできるシステムができた。
  7. 割り算機能を追加しやすくするためにシステムのデザインをさらに修正する(この時点では作業は少し、またはまったくないだろう。引き算とかけ算を実装する時点でデザインが改善されているからだ)。
  8. 割り算機能をシステムに実装する。これで、望んでいたシステムがそれに見合う優れたデザインで実装できた。


電卓ごときでこれをする人はいないと思いますが、私は、このやり方を好んでいます。外部から見た振る舞いやインタフェースが決定した段階では、内部の詳細な設計はできてないことが多いです。おおざっぱなデータの持ち方やロジックがある程度です。実際に作ってみたら、思ったより複雑だったり、簡単だったりします。なので、上記のようなやりかたをすることが多いです。

ただ、これは扱っている範囲が大きくなったときには、通用しにくくなることも、またありがちな話です。

いずれにしても、ショボイ機能であっても、ひとつずつ実装していきたいのには理由があって、

プロジェクトによっては、要件が複雑すぎて最初のバージョンを永遠にだすことができない、ということもある。この状況に陥ったら、機能を絞り込むことだ。

これです。ユーザに公開するかどうかは別にして、足し算までできたら、とにかくメインのブランチにマージしてしまって、本番に近い環境で動作させたいのです。そのためには、メインのブランチから離れている時間を短くしないといけなくて、さらにそのためには、断片的ではあっても動く機能をひとつずつ実装していきたいのです。

要件定義

「この仔馬が月面まで飛べるようにするにはどうしたらいい?」と誰かに訊かれたら、「解決したい問題は何か?」と質問しなければならない。この人は、実際には灰色の石を集めたいだけかもしれない。

これ聞くと嫌がられがちなので、気をつけないといけないんですけどね。それでもなんとか聞き出したいです。本当に必要な機能や作業はなんなのか、っていうのは重要です。

一方で、「ユーザを増やしたいってのが目的らしいけど、こんな機能を入れても増えませんよ」みたいな気分になっても、私は、我慢するようにしています。が、その話はまた、いつか。

まとめ

エントロピーが増大したコードを、きれいにしつつ、機能追加をしていった経験をもつ人の話は面白かったです。最初からきれいなコードを書けるのが理想ですが、実際には、そうなっていないことが多々あります。そういう実践的な立場からの教訓から学ぶことはありますね。




2012-11-04

What's New in Python 3.3


What's New in Python 3.3 のハイライトをコピペ。自称情弱な人のほうが、はるかに詳しそうだけれど、まずは調べるであろうことをリストアップ。

と思ったのですが、 What's New In Python 3.3をざっと眺める にすでにまとまっている気がします。これでいいんじゃないだろうか。

文法
  • generator の delegation
  • u'...' 記法の復活
新ライブラリ
  • faulthandler
  • ipaddress
  • lzma
  • unittest.mock
  • venv
built-in
  • I/O 例外の階層構造
実装の変更
  • import
  • unicode 文字列のメモリ使用量
  • 属性辞書のメモリ使用量
ライブラリの向上
  • decimal モジュールの C アクセラレータ
  • email モジュールでの unicode 処理
セキュリティ
  • デフォルトでハッシュの randomization がオン

2012-10-28

pyspa 2012-10 3日目 〜リファクタリング〜

眠い...

昨日はチューニングっぽいことをした後、眠りにつきました。今日はもう直接仕事をするのではなく、コードのエントロピィを下げる作業をしました。

たとえば、テストコードを見てもらったときに「これは、mock.patch を使いすぎだろwww」と指摘された箇所があります。

class API_UpdateCountsTest(TestBedTestCase):
    @patch('poll.api.get')
    @patch('backend.api.update_options_in_datastore')
    @patch('backend.api.calculate_deltas')
    @patch('google.appengine.api.taskqueue.Queue')
    def test(self, Queue, calculate, update, get_poll):
        ...
        self.assertEqual(Queue.call_count, 3)
        self.assertEqual(Queue.call_args_list[0], ((backend_api.QUEUE_NAMES[0],), {}))
        self.assertEqual(queue.lease_tasks.call_count, 2)
        self.assertEqual(queue.lease_tasks.call_args, ((backend_api.LEASE_SECONDS, backend_api.MAX_TASKS),  {}))
        ... (以下、大量の assert)

まあ、ひどい。テスト対象の関数の中で、いろんなことをしていて、かつ、それが丸見えになっているのが原因です。手順が多いのは仕方ないのだけれど、データの受け渡しがたくさん丸見えなのがよくない。

def _update_counts(queue_id):
    queue_name = QUEUE_NAMES[queue_id]
    queue = taskqueue.Queue(queue_name)
    tasks = queue.lease_tasks(LEASE_SECONDS, MAX_TASKS)
    vote_count = len(tasks)
    option_count = 0
    if tasks:
        payloads = [json.loads(task.payload) for task in tasks]
        deltas = calculate_deltas(payloads)
        option_count = len(deltas)
        update_options_in_datastore(deltas)
        # handle done
        for data in payloads:
            ... (まさかの20行くらい)...
        # delete task
        queue.delete_tasks(tasks)


ひどす。急ぎで性能を上げるために、コードを追加/削除/変更をしたときに、とにかくテストを書いて通すようにした残骸が残っています。こうやってエントロピィが増加するわけです。エントロピィの意味分かってませんが。

というわけで、以下のように変更。

def _update_counts(queue_id):
    data = dequeue(queue_id)
    if not tasks:
        return
    update_options(data)
    update_dones(data)

それでも patch は 3つくらい必要ですが、assert がシンプルになります。

そんな作業をしていたら、夜が明け、海辺まで散歩し、温泉に浸かり、朝食を食べて、もうすぐ10時になります。解散して、帰宅して寝ます。

2012-10-27

pyspa 2012-10 2日目

Python 温泉 2012-10 の1日目 は、Google App Engine の応答が悪くてふて寝で終わったわけですが、午前3時ごろに監視ツールから「正常に戻りました」メールが届いて起床しました。

というわけで、意地になっているチューニングを継続しました。データの流れは、

フロントエンド → タスクキュー → バックエンドインスタンス

になっていました。フロントエンドは自動スケールで台数がいくらでも多くなります。タスクキューとバックエンドインスタンスは 1:1 対応していて、 lease_tasks() している最中に、フロントエンドが add() するのに時間がかかるように見えます。

てなわけで、タスクキュー数とバックエンドインスタンス数 を N:1 にしてみたら、ちょっと応答がよくなってる感じもありつつ、しかし、あんまり優位な差ではないなあ、というところです。ひととおりの変更をしたので、月曜にちゃんと負荷かけてみます。

細々とした修正をしました。月曜に備えて、明日はゆっくりしようと思っています。リファクタリングとか。

pyspa 2012-10 1日目

本日から Python 温泉 2012-10 に参加しています。当初はだらだらと「いつかやる」リストに入っていることをやろうと考えていました。が、仕事で作っているプロダクトの性能要件が、昨日の夜に上がってしまったので、そのあたりをやろうとしています。まあ、なんとかなると思うので、やらなくてもいいんですが、ちょっと意地になっています。

Google App Engine で、アンケート集計をするようなサービスを作っています。アンケートへの回答が秒間5000 くらいです。一瞬だけであればさばけますが、これが長時間続くとキツイなぁというところです。memcache に置ければいいのですが、誰が回答したかをあとで取り出す必要があるので、揮発する memcache には置かない方針です。ちょっと意地になっています。

で、モヒカンたちに相談したところ...
  • 無理だろ m9
  • そういうことに使うなよ m9
  • GAE のキューは datastore なんだから遅ぇよ m9
  • いくらインフラ弱いっつっても、そろそろ潮時だろ m9
と...。だんだん、意地になってきました。

ボトルネックは backends のインスタンスが、 pull キューから lease_tasks()/delete_tasks() するとき、add() にすごく時間がかかることです。数秒かかります。ここをなんとか工夫したいなぁと思っていたのですが...



Python 不安定になっとるやんけ! というわけで、今日は寝る。

2012-10-19

プロジェクトマネージャが知るべき97のこと

「プロジェクト・マネジャーが知るべき97のこと」を読みました。この本は他の97のことシリーズと同じく、複数の寄稿(っていうのか?)を集めたものです。したがって、全体としてひとつの一貫した考え方にはなっていない箇所もあります。複数の視点があるというのは、そういうことです。

プロジェクト・マネジャーの仕事の範囲というのは、本来はどこかで決まってそうですが、実際には組織ごと、プロジェクトごとに、その守備範囲はことなっている印象があります。今の職場でのプロジェクト・マネジャーと、前の職場でのプロジェクト・マネジャーの具体的な仕事の内容は、けっこう違います。なので、そんなことも範疇に含まれるのか、と思うこともありつつ。

最近の個人的な課題は、変更をいかにコントロールするか、です。その視点から見た感想として気になった箇所をふたつ紹介します。

完全な知識という誤った考えは、ソフトウェアプロジェクトのための完全で矛盾のない要求が獲得できるという妄想のことです。現実には、ソフトウェアプロジェクトのライフサイクルのいかなる時点においても、要求が完全にわかることはありません。分析フェーズ、開発フェーズ、保守フェーズ、システムがレガシーであるときですらそうなのです。
(デイビッド・ウッド 完全な知識という誤った考え)

これはなかなかに凹む意見ですが、実際問題としてそういうことなのでしょう。矛盾だらけの要件リストで開発を始めたりしませんが、形式的な手法を用いたりしない限り、無矛盾な要件は得るのは至難の業だと思います。

とくに性能要件があるときです。性能は、多くの場合、インフラやアーキテクチャに大きく依存し、その実現可能性は使えるコストにも依存します。場合によっては、使えるコストというが性能に依存することもあります(100万ユーザを捌けるなら金を出す、など)。ってなわけで、依存関係がループします。

とういわけで、全ての条件が明確ではなくても、どこかから始めなければいけません。

経験豊富なプロジェクト・マネジャーであれば、「高、中、低」というフィーチャーの分類をしません。顧客は何もかも「高」にしてしまうおそれがあるためです。彼らはビジネス価値に基づいたフィーチャーの優先順位リストを好んで使います。すぐれたプロジェクト・マネジャーはビジネスオーナーに対して「遅れが発生した場合に、優先順位リストの下の方にあにあるフィーチャーは、このリリースでは実現できないかもしれません」とくぎを刺します。こうしたやり方は、ビジネスオーナー、特にフィーチャー同士の衝突について学んでこなかった人にとって、苛立ちの原因となるでしょう。ですが時間がたてば彼らもこうしたプロセスに慣れて、プロジェクトの現実として受け入れるようになります。
(ジョー・ゼネビッチ 約束以上にすべきか、約束以下にすべきか)

まあ間違いなく「高」ってつけられます。ABC を使っていると、いつのまにか A+ とか S とかついてます。何かを優先する、ということは、別の何かを優先しないということです。発注側が「全部大事」といって押し込んだとき、受注側は見えないところで手を抜かざるを得なくなります。それは多くの場合、品質やパフォーマンスだったりすることでしょう。

いまだにプロジェクトの中での優先順位付けを、ステークホルダ間で共有する方法というのは持っていません。ゼネビッチが書いているように、理解できない人にとっては苛立ちの原因になりますし、状況によっては単純に受け入れらません。がっかりな話ですが、まあ、そんなもんです。

絶対うまくいく法則というのはないのですが、できるだけ相手に分かる指標に変換するように心がけています。「これもやってほしい」「はい、できます。+1週間です。」というような会話です。+100万円でも、同時アクセスできるユーザ数20%減、でもなんでもいいんですけど。

この本を読んで、正解を見つけるのは大変・不可能かも知れませんが、飲み屋で先輩や同僚の話を聞く、の効率良い版だとコストパフォーマンスはよいと思います。

2012-10-11

git-flow/hg-flow と継続的デリバリの悩み

git-flow/hg-flow を使ったブランチ方針に、不満というか不都合というか、気になることが出てきたのでメモしておきます。結論めいたものはありません。

git-flow はブランチ方針を楽にするためのツールでしかないけれど、この方針のことを git-flow と書いています。

そもそも…

基本的に、git-flow/hg-flow のブランチ方針はよいものです。Jenkins を使って自動テストをやるようになって、ふと気になり始めたことがあるわけです。

develop ブランチに変更があったら、その先頭のリビジョンに対して、単体テスト → テスト環境にデプロイ → 機能テスト → pep8+pyflake の順に実行します。すべて通ったリビジョンはリリース候補です。急ぎのときは pep8+pyflake がしくじっててもデプロイして、後で修正します。

リリース手順は以下のとおり。

hg flow release start XXX    # step 1
hg flow release finish XXX   # step 2
hg up -C master              # step 3
make deploy                  # step 4

リリース頻度は、日によりますが、1日に数回リリースすることもあります。

release ブランチ要らなくね?

git-flow/hg-flow は、step 1 の後で、テストとかドキュメントの更新とかをして、問題がなければ step 2 へ進む、というのを想定していると思います。ですが develop の先頭をテストした後に、release/XXX ブランチを作るので、ここは当然テストが通っています。なので、step 1 と 2 の間で、特にやることはありません。機能を追加したときに changelog を書き換えるくらいです。そんなのあとで書き換えてもいいので、実は relase ブランチは不要だろうという気分になります。

master ブランチをテストしてない

次に、step 2 をしたら、master へマージしています。git-flow/hg-flow をきちんと使っていたら、 master が develop と同じコードになるのって保証されてるんだっけ?という、残念な疑問があります。develop はテストしているけど、 master はテストしてないだろう、と。

そんなわけで大抵、差分を確認するハメになります。

hg diff -r develop:master --stat
 .hgtags |  6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

.hgtags にだけ変更があれば、問題ないってことでリリース。簡単なスクリプトで、このチェックは自動化できる気はします。

考えるのが嫌になってくる

もういっそのこと、trunk (master, default なんでもいいけど) 1本だけで、ブランチきって変更してマージしていくか、という気分になります。なりますが、これもだるい。新機能を鋭意開発中のとき、リリース済のバージョンにバグがあったとき、default の先頭以外のリビジョンから hotfix ブランチをつくり、もとのリビジョンにマージする。そうすると、default の head がひとつ増えて混乱する。マージすればいいんだけど。

git-flow で一番気に入っているのは、事実上リリース履歴になっている master ブランチがあるのと、hotfix ブランチで不安定なコードの影響なく修正ができることなのです。けど、自動化をしようとすると hotfix の取り回しがややこしそう。hotfix の作業を自動化するのは、やめておくか、というところで悶々としています。自動化は手段であって、目的ではないですしね。

2012-09-16

PyCon JP 2012 に参加しました

PyCon JP 2012 に参加しました。昨年は聞くだけでしたが、今年は発表しました。

Trying Continuous Delivery というテーマで、私の継続的デリバリーの試みの軌跡を話しました。英語のプレゼンテーション、全力で疲れて、汗だくです。


 

いくつか質問を受けたので、その応答を残します。


  • [例に出てた] DateProperty を StringProperty に変更するみたいなことしたの?
    いいえ。例が悪いですね。このままやったことはないですが、データ型を変更したことはあります。
  • 受け入れテストではどこまでやってるのか?ステータスコード確認するだけ?
    最初はステータスコードを見てただけ。今は、実際のユーザが行うような API 呼び出しをして、適切なレスポンスボディが返ってくることを確認しています。
  • どんなツール使ってるの? Selenium とか?
    作っているものが Web API なので Selenium は使わいません。requests モジュールで HTTP アクセスを書いています。テストランナは nose です。
  • 負荷テストには何を使っている?
    実はまだローカルのPCを使っているだけ。来週くらいから、 neustar の Load Testing サービスを使う予定です。以前に使ったこともあります。

次は「何かを作りました」みたいな話をしたいなぁとか思ってます。あと、このブログ英語にしてみようかな。今日はもう疲れきってて、いろいろ書いていますが、そんなことは忘れて、昨日までの自分になるかも知れません。

2012-07-29

Jenkins ユーザ・カンファレンス 2012 東京で話を聞いてきました


John Smart「Jenkins によるよる自動受け入れテストから継続的デリバリーまで」


ビジネスゴールに貢献する価値に集中する。ビジネスに貢献しないことは、すべて無駄だ。
Business Goal -> Features -> Stories -> examples へブレイクダウンしていく。

これはリーンスタートアップや、アジャイル開発なんかの考え方に近いと思います。

受け入れテストの自動化が、リリースサイクルを速くする。
メトリクスがしきい値を下回ったら、ビルド失敗にする。たとえば、コードカバレッジなど。

テストを自動化する時間 < 手動でテストをする時間、が成立するというのが条件だと思います。でないと、抵抗にあうんじゃないでしょうか。その見極めが重要だと考えています。

もうひとつ気にしているのは、そういう受け入れテストをしやすい設計にする、という仮説です。作りやすさではなくて、テストのしやすさ、すなわち、価値の測りやすさを、設計の方針に組み込むのがいい、という仮説を持っています。

出荷可能であるために、品質が高いこと、そして、品質が高いことを確信しているとことが大事。
どのくらいの品質なのかを知っている、という状態にしたいのです。バグがあるのが分かっていて、それがどの程度プロダクトに影響があるのか、そして、ビジネス価値に、どの程度影響するのか、が前もって分かっていて、受け入れ可能であれば、リリースできると思っています。ので、こういう考え方には、勇気づけられました。

R. Tyler Croy 「飛行機を飛ばしながら直す方法教えます:JenkinsとGerritによる継続的デプロイメントの実践」


反復的に構築していくプロセスなのである。

手動でデプロイしている会社に入って、開発を続けながら、継続的デプロイメントを浸透させていった過程の話でした。これを一番聞きたいのです。

リリースの何%が失敗して、リリースあたりどのくらいコミットがあって、とかを最初に測定しているのがすごいですね。でないと、効果が分からりませんからね。

7台並列でやって、テストが終わるまで15分かかる。これは、本番サーバに問題があってたときに、修正するまで最大で15分かかるってことだ。

なるほどなぁ。テストに時間がかかるということは、ビジネス価値の提供が遅れるということでしょう。ビジネス価値につなげる考え方を意識せねばなぁと思います。

データマイグレーションが難しかった。データマイグレーションが許される期間があって、その期間以外はマイグレーションがあったら fail させる。マイグレーションが発生したときには、人間がレビューするようにしている。100% 自動化するのは、テストやステージング環境のデータは、本番と違うので、無理だろう。なので人間の目が必要。

ここはやっぱり難しいですよね。KVS の場合はスキーマによる制約がないので、以前書いたエントリのような、アプリケーションでのアプローチが使えますが、RDB だと難しいですね。

まとめ

絶対完璧なプロセスをいきなり導入しようとするのではなく、反復的にだんだん作っていくこと、ビジネス価値に集中すること、がふたりの共通点だったと(勝手に)思いました。というわけで、私も徐々にやっていきます。まとまったら、どこかで発表したい。

(追記)
そうそう。むやみにプラグインを使うのではなく、Makefile で動くようにしておいて、Jenkins はトリがで make して通知するようにしておくといい、というアドバイスというか実践をしている、という話を聞きました。Jenkins の設定がいっぱいあると大変そうなので、Makefile で解決するのはいいなぁと思いました。通知に何を使うかを聞いたら、XMPP だそうで。にゃるほど。

Python Developers Festa 2012.07

Python Developers Festa (pyfes) 2012.07 に行きました。完全に個人的なメモです。

今回は発表することもなかったので、知り合いとダベることと、気になっていることをちょろっと試すことにしました。

普段、仕事をするときには、目の前の問題を、できるだけ確度の高い予想期間で片付けたいので、手持ちのカードだけで解決しようとしてしまいます。たかだか数時間ですが、ちょろっと試す機会に使おうと思ったわけです。自宅だと、ついエロ動画サイトとか徘徊しちゃうんでね。

Git

すみません、今更です。とりあえず、空のプロジェクトを作って、いじいじしました。Git と、いつも使っている Mercurial は、ブランチの考え方が違うので 1 on 1 でコマンドが対応しません。(1) add の意味の違いと、(2) push でどのリポジトリに、どのブランチを、っていうのを指定するのが、いちばん私が気をつけねばと感じたことです。

PyCharm

いまは Komodo Edit (Komodo IDE の機能限定無料版)を使っています。以前 @ikasamt に Pycharmいっすよと言われていました。試用版をインストール。

Backspace にCTRL-H 、Enter に CTRL-M を割り当てる。

App Engine のプロジェクトを作ったのだけれど、最近は buildout で環境を使っているので、Komodo も Pycharm の App Engine インテグレーションはじつは不要だったりする。単体テストやデバッガを使うときに効いてくるのかも知れない。

watchdog モジュール

ドキュメントもコードも、ファイルを変更したらビルド/テストするようにしています。が、これが美しくないやりかたをしています。OS X 上でのEmacs や Komodo でのファイル変更は、OMake で検知できません。

watchdog インストールして、さっそくやろうとしたら…

$ bin/watchmedo shell-command --patterns="*.c;" --recursive --command='make all'
Traceback (most recent call last):
…
    from argh import arg, alias, ArghParser
  File "/Users/torufurukawa/works/pyfes201207/lib/python2.5/site-packages/argh/__init__.py", line 25
    from .exceptions import *
SyntaxError: 'import *' not allowed with 'from .'

面倒くさいなぁ。もういいや。

ndb

GvR が作った、Google App Engine のデータストアライブラリ(あるいは、BigTable のラッパ)。この文書をだらだら眺めていました。Pythonic で使いやすいし、隠蔽のバランスが直感的です。

ところで未だに、キーをヒエラルキーにすることが活かされる状況って、どういうときだよ、と思っていました。トランザクションで要るとは思うんだけどなぁ、などと、昼ごろにうとうとしながら考えていました。

あー、エンティティではなくて、キーから親子関係を引けるのがいいのか、と。
キーに親子関係がないときは…

from google.appengine.ext import ndb
class Foo(ndb.Model):
    pass

class Bar(ndb.Model):
    foo = ndb.KeyProperty(kind=Foo)

f = Foo()
f.put()
b = Bar(foo=f.key)
b.put()
k = b.key

# k から f を取得する。
b = Bar.get_by_id(k.id())  # ここでフェッチ
f = b.foo.get()  # ここでもフェッチ

キーに親子関係があると

from google.appengine.ext import ndb

class Foo(ndb.Model):
    pass

class Bar(ndb.Model):
    pass

f = Foo()
f.put()
b = Bar(parent=f.key)
b.put()
k = b.key

# k から f を取得する
f = b.key.parent().get()  # フェッチはここだけ

だべり

  • とおるメモを、電子書籍にして売れば? → いいなぁ。このブログのアフィリエイトよりも、遥かに効率がよさそうだ。
  • とおるメモアプリは、電話機能にアクセス権限とか要るんでしょ? → w
  • アイコンはち◯こだろ → w
  • うちの製品のマーケティングって、次は何したらいいの? → (・∀・)ニヤニヤ

2012-07-18

データモデルが変わるときのデプロイ

運用中のウェブサービスで、保存してあるデータのスキーマを変えたいときがあります。このとき、プログラムのコードとデータベースの間で互換性が保ちつつ、双方を更新する必要があります。Google App Engine を使ったサービスでのやったことのメモです。



プロパティの増減

最初、こんなデータがあったとします。

from google.appengine.ext import db

class User(db.Model):
    name = db.StringProperty()

これでたくさんのデータが保存されている状態になったとしましょう。ここで、プロパティが増えます。

class User(db.Model):
    name = db.StringProperty()
    birthday = db.DateProperty()

Google App Engine のデータストアには ALTER TABLE みたいなのがありません。が、こういう場合はデータストアに何もしなくていいです。birthday を指定せずに保存されたエンティティは birtyday に None が入っています。

プロパティが減る場合は、単純にモデルの定義からプロパティを消せばいいでしょう。データストアには残っているけれど、あっても害がないから。けど、後々、違う型で復活したりすると、よろしくない気がします。

クエリが増える

プロパティを変更したととき、あたらしいクエリを発行することになった場合には、 (1) インデックスの更新してから、(2) コードをデプロイします。

これまで使っていなかったけど、新たに以下のようなクエリをするようになったとしましょう。

User.all().query(birthday<xxx).query(name<yyy) 

dev_appserver をローカルで動かして、このコードを一度でも実行すると index.yaml が更新されます。以下のコマンドで App Engine のインデックスを更新します。

appcfg.py update_indexes .

ダッシュボードの Datastore Indexing のページを見ると、Building… と書かれている箇所があります。何度かリロードして、Ready になってから、appcfg.py update します。先にインデックスを更新しないと、アプリケーションコードがクエリしようとしたとき、「インデックスができてねーよ」エラーが出ます。


より複雑な変更


ほんとに困るのは、プロパティの型が変わってしまうとか、複数のプロパティでひとつの状態を表したくなるとかいう場合です。


class User(db.Model):
    name = db.StringProperty()
    birthday = db.StringProperty()  # DateProperty から StringProperty へ変更

このコードをデプロイして user.birthday にアクセスしたとき、以前の DateProperty で保存されていると、ここでエラーが出ます。だからといって、先にデータを書き換えてしまうと、現行コードでエラーが出ます。

書籍「継続的デリバリー」は、新旧どちらスキーマでも動くようにアプリケーションを変更したあと、データベースのスキーマを変更する、という方針を提案しています。 Google App Engine のデータストアであれば、新旧どちらのプロパティの型であってもいけるようなモデルに変更する、ということになります。

class User(db.Model):
    name = db.StringProperty()
    date_birthday = db.DateProperty(name='birthday')
    str_birthday = db.StringProperty(name='str_birthday')

    def get_birthday(self):
        if self.date_birthday:
            return self.date_birthday.strftime('%Y-%m-%d')
        else:
            return self.str_birthday

    def set_birthday(self, val):
        if isinstance(val, date):
            self.str_birthday = '%Y-%m-%d' % (val.year, val.month, val.day)
        elif isinstance(val, str):
            self.str_birthday = val
        else:
            raise TypeError
        self.date_birthday = None

    birthday = property(get_birthday, set_birthday)

ハンガリアンっぽくてキモいです。あと文字列に変換するところは、関数に切り出したほうがいいですね。で、これでデプロイします。データの個数が少なければ remote_api で、多ければ mapreduce のライブラリを使って、プロパティの型を変更します。すべてのデータが新しい型に対応したら、不要なコードを取り除いてデプロイします。

class User(db.Model):
    name = db.StringProperty()
    birthday = db.DateProperty(name='str_birthday')

と、偉そうに書いたものの、最後の書き換えをせずに残っている箇所もあります。

 

2012-06-28

CORS に対応する nginx 設定

CORS (Cross-Origin Resource Sharing) について以前調べました。

ブラウザは、実際に GET や POST する前に、プリフライトリクエストというリクエストを発行します。通常これは OPTIONS メソッドです。で、このときのレスポンスヘッダをみて、ブラウザは呼び出す/呼び出さないを決定します。
http://torufurukawa.blogspot.jp/2012/05/http-access-control.html

これを実現するのに nginx (1.2.1) の設定で困ったのでメモしておきます。ちなみに、このウェブサイトは Basic 認証をかけます。

http {
     #...
     server {
          listen 8080;
          server_name localhost;
          location / {
               root html;
               index index.html index.htm;

               # GET, POST 用のヘッダ設定
               add_header Access-Control-Allow-Origin *;
               add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
               add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
               add_header Access-Control-Allow-Credentials true;
               # Basic 認証設定
               auth_basic "xaxtuxo";
               auth_basic_user_file  "/usr/local/etc/nginx/.htpasswd";

               # OPTIONS 用の設定。ヘッダの指定をここにも書かないといけない。
               if ($request_method = OPTIONS ) {
                    add_header Access-Control-Allow-Origin *;
                    add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
                    add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
                    add_header Access-Control-Allow-Credentials true;
                    add_header Content-Length 0;
                    add_header Content-Type text/plain;
                    return 200;
               }
          }
     }
}

OPTIONS メソッドの設定を書いていなくて、最初、動きませんでした。また、 if ブロックの中にも、add_header をいちいち書く必要があります。

参考

2012-06-27

システム発注側にとって要件は自明ではない


ズボンの股のあたりに穴があいて久しいので、服を買おうと街に繰り出しました。が、もうどうしていいか分からない。どんな色がいいか、どんなスタイルがいいかとか、全くわからない。今年の新色とか、デザインとか言われても、判断のしようがないんですよね。うっかり高そうな店に入ると、すぐに店員が話しかけてきて、要らないものを紹介してきますし。

顧客は自分が何が欲しいか分かっていない。顧客が自分で何が欲しいか分かっていると期待するのはやめることだ。(ジョエル・スポルスキー「ジョエル・オン・ソフトウェア」)

「何が欲しいのかは分からないが、これではないことは分かる」みたいな言い回しをどこかで聞いたのですが、思い出せません。とにかく発注側が要件が分かっている、と思ってはいけません。おそらく分かっていません。開発者自信が自分で使うライブラリやフレームワークを作る、みたいな状況でないかぎり、おそらく分かっていない、という前提くらいがちょうどいいと思います。

もちろん受注側はプロなので、まともな会社なら、ちゃんと考えてやってくれるでしょう。きっと素晴らしい提案が出てくるはずです。それでも、なんというか、自分たちが最終的に何を依頼しているのか、っていうのは、把握しておきたいなあ、と思う次第です。

ぜんぜんまとまってないし、以前にも似たことを書いたのですが、買い物をしようとして痛感したもので、もういちど書いておくことにします。

2012-06-14

運用の継続的改善

ウェブサービスをリリースしたら、当然その運用が必要です。しかし、これがよく分かっていません。だいたい当初の想定にはモレがあって、後で困ります。経験と知識と思考の不足がなせる技です。

だからと言ってほったらかすわけにいかないので、一時期、毎日朝の1時間は運用の改善に当てていました。Skype やメールを見る前に、なにか改善する。改善は、1時間でなんらかの形になることにしていました。ちょっとしたスクリプトを書くこともありましたし、ダッシュボードを作ったりもしました。運用に便利な機能を、組み込んだりもしました。

また、同じようなことを最近始めました。いまでも運用は苦手です。ですが、こうやって毎日ちょっとずつやっていくうちに、じわじわ改善されていくのは嬉しいものです。毎日1%ずつ効率化していくと、1ヶ月たてば20%の効率化になります。

自社サービスの場合には、そういう時間の使い方ができるので、最初は苦しいですが、地味に続けていくと、ある時「おー、楽になったなぁ」となります。

最初から全部できればいいんですが、残念ながらできていないので、銀の弾丸を探さずに、ちまちまやってます。

2012-06-13

Things beta を使い始めました

TODO管理アプリの Things を使っています。Mac、iPhone、iPad にそれぞれアプリがあって、Omni Focus ほど重量級ではないけれど、GTD でいうところのプロジェクトまで管理できます。

最大の難点は、デバイス間での同期には互いにローカルのネットワークで見えていないといけない、ということです。クラウドで同期するベータ版が公開されていたのですが、この度、βのままながらも、これまでのデータをβアプリに移行する機能が公式にサポートされました。

というわけで beta の使用を開始。勝手に同期してくれるのは、ほんといいですね。

2012-06-12

boto で S3 からダウンロード

Amazon S3 に保存されたデータをダウンロードする、という作業を自動化することにしました。認証周りを自分でやるの面倒だったので boto を使うことにしました。

$ python
>>> from boto.s3.connection import S3Connection
>>> conn = S3Connection('<aws access key>', '<aws secret key>')
>>> conn.get_bucket('wozozo')
<Bucket: wozozo>
>>> from boto.s3.key import Key
>>> k = Key(b)
>>> k.key = 'path/to/resource'
>>> fout = open('filename', 'w')
>>> k.get_file(fout)

簡単すぎる。

2012-06-09

私が受け入れテストを自動化しない4つの理由

なんの自慢にもならないどころか、完全に自戒エントリです。まず、そもそも受け入れテストを自動化する試みは有効だという仮説を持っています。

言葉の定義が曖昧で申し訳ないですが、V字モデルを考えた時に右上のほうにあるテストのことを想定しています。ですが、あまり厳密にシステムテストとの違いとか、パフォーマンスや負荷はとかを区別していません。「右上のほう」くらいを考えます。

要求分析 -------> 受け入れテスト
 基本設計 -----> システムテスト
  機能設計 ---> 結合テスト
   詳細設計 -> 単体テスト
       コーディング

反復的に、スパイラルになど、とにかく少しずつソフトウェアを開発していくのが、バグ発生のリスクが少ない印象があります。仕様変更にも耐えやすい。そうすると、受け入れテストが自動化されていると、容易にテストでき、要求とことなる状態になったことがすぐわかります。あるいは、要求との差異の大きさが分かります。なので、受け入れテストは自動化したほうがよいわけです。

が! ほとんどできていません。システムテストでさえ、網羅率が非常に低いのが現状です。これだけ頭でわかっているのに、やっていないのは、なぜなんだろうと考えました。おそらく、レイヤが高いところでのテストは、投資の規模が大きくないと、便益が得られないからだ、という仮説です。

1. 実装に時間がかかる

単体テストなんかは、ほんの数行のコードを書けば、とりあえず何がしかのテストを始められます。自分だけではじめることもできます。一方で、受け入れテストはそもそも手順が多いし、前提条件を作り出すために作業やコーディングが発生します。なのでめんどくさいなぁ、と思ってしまいます。

2. 実装に費用がかかる

単体テストなら開発マシンで実行するところから始められます。受け入れテストは、特にサーバで動かすときには、テスト用の環境を用意する必要です。テスト自体が副作用を生むので、他の作業から隔離する必要があるからです。

3. コミュニケーションに時間がかかる

要求なるものが、要求する側とされる側で、そのまま共有できることは稀です。すくなくとも、私の職場では。何らかの形で文書の変換が必要になります。PowerPoint で「友達がつぶやいたら盛り上がっている絵」みたいなのが書かれていたとして、それを、「N秒以内に友達関係にあるユーザが、システムに対して操作Aをしたことが、画面に表示される」と翻訳しないといけません。逆に、後者だけあっても、受け入れOKを確認する人には通じません。これはけっこう難しくて、いまだに効率良く要件を記述する方法が分かりません。

4. 変更の追跡に時間がかかる

関連して、変更があったときの追跡に時間がかかります。いつのまにか PowerPoint のファイルに「なんとかボタン」がひとつ追加されていたりして、そのボタンを機能させるためには、かなりの影響範囲があったりするわけです。それを追跡して仕様に反映するのだけでも大変なのに、テストとかもうああ、という。

書きだしてみて、ネガティブスパイラルっぷりにびびります。解決策や結論的なものは、残念ながらまだありません。ただ、仕事の大きさに圧倒されて、手を付けられないのであるなぁ、というのが分かったというところです。

2012-06-06

git-daily 的なブランチ方針がしっくりきました


ソースコード管理でのブランチの切り方を模索してきました。いったん落ち着いたので書き残しておきます。

前提条件は以下のとおりです。

  • iPhone アプリがアクセスするウェブAPIを開発する。
  • サーバサイドの開発者はふたり。大阪と東京。
  • 他の案件とかけもち。


行き着いたところ

  • Mercurial
  • git-flow/git-daily に似たブランチ方針
    • 機能追加、開発中機能のバグ修正: default ブランチから、XXX ブランチを切って機能追加/変更し、default へマージする。
    • リリース: default ブランチから、release/XXX ブランチを切って、ステージングでテスト/修正。終わったら、default と master へマージし、master の先頭を本番サーバにデプロイする。
    • リリース済不具合の修正: master ブランチから、hotfix/XXX ブランチを切って、ステージングでテスト/修正。おわったら、default と master へマージ。master からデプロイ。
    • XXX はチケット番号
    • タグづけしない

この方針がしっくりくるまで、紆余曲折がありました。

Phase 1: BP Mercurial Workflow

週末に何をしているか謎な ぁっぉ氏で有名な、ビープラウドで採用されている BP Mercurial Workflow で始まりました。

すべての作業は default ブランチから、チケット番号に対応するブランチを作り、終わったら default ブランチに戻ります。

  • 新機能追加や不具合修正のブランチは、テストが終わったり、リリースする直前に default ブランチへマージする。
  • リリースするときには必要に応じて、default ブランチからリリース用にブランチを作る。確認が終わったら default ブランチにマージする。
  • マスター・リポジトリとリリース・リポジトリを用意する。
  • マスター・リポジトリの default ブランチの先頭がリリース可能な状態になったら、リリース・リポジトリに push する。
  • リリース・リポジトリの default ブランチ先頭は、常にリリース可能である。


リポジトリをふたつ用意する、という運用を、実施したことはありません。最初、このリポジトリの意味を勘違いして、てっきりプロダクション環境にコピーしたリポジトリだと思っていました。

このブランチ戦略のいいところは、とにかくシンプルなことです。ブランチ名はチケットに対応づいているのでユニークですし、個々の作業が完了したら default に戻せばよいのです。

このやり方に馴染めなかったのは、自分がどの環境にデプロイされている機能を変更しているのか分からなくなるからでした。

頻繁なリリース、頻繁なバグフィックスが特徴の開発でした。バグがある状態でリリースすんなっていうのは、ごもっともです。iPhone アプリ側に不具合があったとき、すぐに修正が効かないし、大多数がアップデートするまで時間もかっかります。ですので、サーバサイドでなんとなく頑張るようなこともあります。

また、新機能を鋭意開発中に、別の仕様が飛び込んできたりもします。開発中のコードに手を入れることもあります。default ブランチと、そこから派生したチケットブランチだけでは、ツリーを見た時に「いま本番サーバでデプロイされているコードがどれか? ロールバックするとしたら、どのポイントなのか?」が、私には直感的に分かりませんでした。タグづけとかもしたんですけどね。

そんなわけで、開発中とデプロイ済のコードを、ブランチで分けてあるといいなぁと思いました。

Phase 2: git flow

次に試してみたのが、A successful Git branching model です。これを支援する git-flow という git 拡張があります。

開発用とリリース済用の、ふたつのブランチを使う方針です。

  • 機能追加、開発中の不具合修正: develop ブランチから feature/name-of-feature ブランチを切って作業し、develope ブランチへマージ。
  • リリース: develop ブランチか release/x.y.z ブランチを切って作業し、準備ができたら default と master へマージ。x.y.z とタグをつけて、master からデプロイ。
  • リリース済の不具合修正: master ブランチから、hotfix/x.y.z ブランチを切って作業し、develop と master へマージ。x.y.z とタグをつけて master からデプロイ。


この方針では、master ブランチの先頭が、いまデプロイされているバージョンです。master ブランチのひとつ前のコミットが、その前にデプロイされたバージョンとなります。

develop ブランチは基本的に不安定で、release ブランチでの作業をへて安定し、master へマージする、というものです。これで遠慮無く devepopブランチに変更を反映できますし、master ブランチ(リリース済みのコード)の修正にも影響されずに、リリース用にコードを整理できます。

すばらしい! と思ったわけですが、1日に数回リリースすることもあって、バージョン番号とか面倒なのですね。また、master ブランチの各リビジョンが、基本的に本番サーバでのリビジョンなので、タグとか要らないんじゃないのか、と思ったりもしました。

Phase 3: git daily

@sotarok による git-daily の紹介を聞きました。git-daily はGREE での開発フローと、その支援をするツール。1日に複数回リリースすることだってある、というのが前提。これは、私の置かれている状況と似ています。

  • 重要なのは gitflow というツールではない
  • tag とか切らない
  • リリースブランチ: release/yyyymmdd-hhMM
  • 当時は hotfix と feature は未実装だった。今はhotfix が実装済。


これでかなり、状況に合った方針になりました。けど、ひとつだけ、ほんとひとつだけ馴染まないことがありました。

複数の案件に関わっていて、割り込みが多く hotfix の作業を1日で終わらせられるとは限らない。私があるバグの修正をしている間に、もう1人の開発者が別の機能を実装しおわって、リリースすることもあります。作業開始の日付と、実際にリリースされる時間の順番が一致しない。日付的なもの書かれていると、順番になっていない気になります。「5月7日にリリースした、20120501-1200 ブランチだけどさ」みたいな。気持ちの問題なんですけどね。桁数もおおいし。

前述のプレゼン資料を見てみると、こんなことが書かれています。

会社で使うならその会社での開発スタイルにあわせた Wrapper ツールは必要
gitflow をつかうもよし、git-dailyをつかうもよし、自作するもよし

Phase 4

そんなわけで行き着いたのが、日付の代わりにチケット番号を使えばよいではないか、と。

feature/XXX は作りません。元のブランチから分岐させて、変更し、もとのブランチに戻すというシンプルな作業だからです。SourceTree みたいなツールを使った時、 feasture/XXX みたいなブランチ名にしておくと、まとめてくれるんで便利かも、と最近思い始めています。

hotfix と release のBranch操作が手順が多かったので、hg のラッパスクリプトを書きました。超やっつけ。pelo っていうコマンド名は (ry

# coding: utf8
"""pelo
pelo branch management wrapper commands
"""
from datetime import datetime
from mercurial import commands
HELP_DOC = """COMMAND OPERATION [label]
pelo release start uXXXX .... start new release
pelo release finish ......... finish current release
pelo hotfix start uXXXX ..... start new hotfix
pelo hotfix finish .......... finish current hotfix
"""
def pelo_cmd(ui, repo, cmd, op, label=None, **opts):
if cmd == 'release':
return release(ui, repo, op, label, **opts)
elif cmd == 'hotfix':
return hotfix(ui, repo, op, label, **opts)
else:
_write(ui, HELP_DOC)
def release(ui, repo, op, label, **opts):
prefix = 'release'
start_at = 'default'
return operate_branch(ui, repo, op, label, prefix, start_at, **opts)
def hotfix(ui, repo, op, label, **opts):
prefix = 'hotfix'
start_at = 'master'
return operate_branch(ui, repo, op, label, prefix, start_at, **opts)
def operate_branch(ui, repo, op, label, prefix, start_at, **opts):
if op == 'start':
return start_branch(ui, repo, label, prefix, start_at, **opts)
elif op == 'finish':
return finish_branch(ui, repo, label, prefix)
else:
_write(ui, HELP_DOC)
def start_branch(ui, repo, label, prefix, start_at, **opts):
"""start prefix branch"""
# exit if label is not provided
if not label:
_write(ui,
'abort: You must specify ticket to start %s branch.' % prefix)
return
# exit if not in default branch
if _get_current_branch(repo) != start_at:
_write(ui,
'abort: Run "hg update default" before starting %s branch.' % start_at)
return
# branch <prefix>/<label>
commands.branch(ui, repo, '%s/%s' % (prefix, label))
# commit to mark start
commands.commit(ui, repo, message='started %s/%s' % (prefix, label))
def finish_branch(ui, repo, label, prefix='hotfix', **opts):
"""finish hotfix branch"""
# 変更が残っていたらエラー
status = repo.status()
for l in status:
if len(l) > 0:
_write(ui, 'abort: Your workspace has uncommit content.')
return
# label が指定されていたらエラー
if label:
ui.write('abort: You cannot specify branch name\n')
return
# <prefix> ブランチじゃなかったらエラー
if not _get_current_branch(repo).startswith('%s/' % prefix):
_write(ui,
'abort: you must be in %s branch' % prefix)
return
# determine current branch
current_branch = _get_current_branch(repo)
# merge into default branch
error = _merge(ui, repo, current_branch, 'default')
if error:
return
# merge into master branch
error = _merge(ui, repo, current_branch, 'master')
if error:
return
# notify
_write(ui, '%s was successfully finished.' % current_branch)
def _get_current_branch(repo):
ctx = repo[None]
current_branch = str(ctx.branch())
return current_branch
def _write(ui, message):
ui.status(message + '\n')
def _merge(ui, repo, from_, to):
commands.update(ui, repo, to)
error = commands.merge(ui, repo, from_)
if error:
return True
commands.commit(ui, repo, message='merged %s -> %s' % (from_, to))
cmdtable = {
'pelo': (pelo_cmd, [], HELP_DOC),
}
view raw hgpelo.py hosted with ❤ by GitHub

さいごに

念のために書いておくと、最後の方針が、ユニバーサルにベストな方法だとは思っていません。だいたい職場の開発方針や開発プロセスが変われば、ブランチ方針も変わるかも知れません。 開発スタイルに合わせた、ブランチ方針があると思っています。

「パターンによるソフトウェア構成管理」という本を参考にしました。想定しているツールが Subversion あたりなので、Git や Mercurial などにはバッチリこないこともあります。デスクトップアプリのようなのを想定しているので、メジャーバージョンごとに、リリースブランチがあったりとか。あくまでパターンの紹介なので、抱えている課題と合うものを採用するものだと思います。



2012-06-01

Google App Engine の backends のデプロイ

Google App Engine の backends のデプロイで困りました。
$ appcfg.py update .
が成功するのに、変更した挙動が backends サーバに反映されていないように見えました。そして、実際に反映されていません。
$ appcfg.py backbends . update
のように、backends コマンドに update アクションをつけて実行します。backends.yaml の設定を反映させるだけに使うと思っていたんだけど、コードのデプロイをする役目もあります。

GAEアプリで継続的インテグレーションを試みた雑感


2011年11月の Python 温泉でテストの実践方法を教えてもらう機会がありました。

初めて参加した Python 温泉で、 @voluntas と @aohta に開発プロセスの教えを乞いました。いろいろ教えてもらった中で、実際に手を動かし始めたことを書きます。

その後日談です。

単純だけど動く buildout.cfg を書けるようになったことが、今回のツール知識の中で最大の成果です。

温泉では buildout を使ってテストする側の環境をいっぱつで作れるようになりました。温泉から帰ってから、テストされる側、すなわち、製品側の環境用の buildout.cfg を書きました。

対象となるプロジェクトでは、Google App Enigne を使っていて、本番用と開発用にそれぞれ App Engine アプリを用意していたので、環境絡みの問題がすぐに分かりました。

大したコードではないので自社ライブラリにしなくていいんじゃ、と思ったのです。が、「call_api のテストだけ書いておけば、[simplejson などの] 依存先の使わない機能の不具合を無視できるのだから、自社ライブラリの動作確認だけすればよいでしょ。だから自社ライブラリにしちゃえ」という教えでした。これが一番私にとって重要な考え方でした。

いたく感動していた割に、まったく実践できていません。そして相変わらず車輪の再発明です。素振り、勉強としての車輪の再発明には意味があると思いますが、問題にしているのは避けるべき再発明なのですね。

ウェブAPIを作ることが多いので、製品側のコードも、テスト側のコードも共通化できることは確実にあるのです。なんでやっていないんですかね。なんか面倒そう、と思っているのでしょう。そして、その場で面倒をさけて、あとで面倒になるというわけです。

問題は、Twitter に問題があると、テストが FAILすることです。それは Twitter の問題であって、私が開発しているアプリケーションの問題ではありません。
なので、OAuth が成功した or 失敗したふりをしてくれるモックが必要です。それが次の課題だろうな、と考えています。

これも、ほったらかしです。具体的に何が必要かというと、OAuth Service Provider のインタフェースとモックだけ実装すればいいはずなんですけどね。今後の課題です(放置フラグ)。

何をしようとしているかというと、継続的インテグレーションをしたいのです。サーバサイドのエンジニアが少ないので、できるだけ自動化したいわけです。人間は創造的なことに時間を使うべきだ、と個人的に信じています(それでまあ、例の対談とかの流れになるわけです)。そのためには自動的に環境構築して、テストできるようにする必要があるのですが、ずーっと止まっていました。今回の温泉で、一歩目を踏み出せたのは大きな収穫でした。

デュバルらによる「継続的インテグレーション入門」では、「Integrate ボタンいっぱつで、ビルドできるようにせよ」というのが一貫した主張で、各要素の作りかた/考えかたが書かれています。

まず Jenkins を導入しました。リポジトリに変更があったら、ユニットテストの実行、GAE 開発アプリ空間へのデプロイ、機能テストの実行までをやりました。インスペクションとデータベースのインテグレーションも、ビルドに含むべきであると書かれていますが、そこまでは到達できていません。pep8 を手動では知らせてチェックインしていました。

データベースのインテグレーションが問題です。理屈の上では、古いバージョン用にデータセットが格納されていて、スキーマ(的なもの)が変更されるのであれば、その変更スクリプトを走らせて動かす、ってことになるのだと思います。あと、GAE の場合はインデックスの更新をリクエストする必要があります。

これはテストデータのセットを用意できていることが前提ですね。確かに、用意しないといけません。なんか作業が膨大そうに見えて、怖気づき、躊躇しています。

このプロジェクトは、継続的インテグレーションの実践の場になり、いくつかのことを学びました。まず何よりも、自動的にテストが走るのは非常に楽である、ということ。API呼び出しによる副作用が複雑で(これはこれで問題ではある)ちょっとした仕様変更でも、意外な影響範囲があったりしました。そういうときでも、テストがあるので安心して変更できました。ただし、テストの数がある程度たまるまでは、なかなかきつかったです。

一方で、継続的インテグレーションをぜんぶ実践するのは、大変そうに感じました。じつは大変ではないかかも知れません、一度走り出したら大変ではなくなるのかも知れませんし、実際に大変なのかも知れません。


2012-05-31

App Engine の NDB に StringListProperty がないっ

Google App Engine で新しくアプリをつくることになったので、調子にのって ndb を使おうとして、いきなり泣きを見ました。
class Foo(db.Model):
    def tags = db.StringListProperty()
というような、文字列配列を持つような StringListProperty が、ndb にはない! 焦りつつぐぐったら、すぐに見つかりました。repeated というキーワード引数つきで定義すればよいみたいです。
class Foo(ndb.Model):
    def tags = db.StringProperty(repeated=True)
ある値を含むエンティティをクエリするには、以下のようにします。
Foo.query(Foo.tags == 'python')
複数の値のうち、いずれかを持つエンティティをクエリするには、以下のようにします。
Foo.query(Foo.tags.IN(['python','ruby']))
Foo.query(ndb.query.OR(Foo.tags == 'python', Foo.tags == 'ruby'))

参考:

2012-05-30

HTTP Access Control のヘッダ

Basic 認証のかかったウェブ API に対して、別のドメインから jQuery.ajax() でアクセスしようとして、えらく時間がかかりました。

サーバに
  • Access-Control-Allow-Origin: http://example.com
  • Access-Control-Allow-Methods: GET, POST, OPTIONS
  • Access-Control-Allow-Headers: Origin, Authorization, Accept
  • Access-Control-Allow-Credentials: true
をヘッダで返してもらうようにしたら、うまくいきました。が、これはただのコピペ。

それぞれ、何の意味を持つヘッダなのかが、 HTTP access control による、Cross-Origin Resource Sharing の解説に書かれています。

Access-Control-Allow-Origin

HTTP サーバに対して、 JavaScript からアクセスしてもよいドメインを指定する。Ajax で same origin 問題で検索すると、たいてい、このヘッダについて言及がある。ここまではよろしい。

ブラウザは、実際に GET や POST する前に、プリフライトリクエストというリクエストを発行します。通常これは OPTIONS メソッドです。で、このときのレスポンスヘッダをみて、ブラウザは呼び出す/呼び出さないを決定します。以下、同じく概要。

Access-Control-Allow-Methods

発行してもよいメソッドのリスト。OPTIONS と GET しか許さない場合には、OPTIONS, GET など。

Access-Control-Allow-Headers

サーバに送信してもいいヘッダのリスト。認証情報は Authorization ヘッダに書かれるので、このヘッダに Authorization が記載されている必要がある。

Access-Control-Allow-Credentials

名前だけみると上の Authorization と冗長な気がするんだけれど、こっちは cookie を送っていいかどうかのフラグ。

ブラウザによって、できたり/できなかったりして、いまだに確信が持てない。

2012-05-29

要件定義の責任は発注側で持つ


開発するソフトウェアの発注内容に矛盾が含まれることがあります。びびる話ですが、残念ながらあります。モレがあるなんてことは、当然のように起こります。

ここで僅かなサンプルから、ものすごい一般化をしますが、日本では発注者がクソである、という仮説があります。ありますって、勝手にいまでっちあげたんですけど。大規模検査/管理システムを構築するメーカの人曰く「ドイツの会社は数百ページにわたる要件定義書を渡してくる。同じようなシステムに対して日本の会社は3ページくらいで済ませてくる」と。日本の発注者、というような集合に共通の特性があるわけあらへんやろう、という感じではありますが。けれど、私の観測範囲では SI の委託側と受託側の互いの期待がずれているために、不幸が起こっているように見えるのです。そして、発注側がけっこうクソなんじゃないのか、って感じがするのです。

SIer に仕事をたのむのは、何らかの事情で自分たちができないからです。時間とか、外注のほうが安いとか、技術がないから、とか。まあ、いろいろあるでしょう。そして、発注側に技術や能力や機能がないとき、発注側が要件定義できない場合、ということがあり得るのです。それはべつに構わない。

問題は、発注する側が (1) 要件定義できていないことを自覚していない、とか (2) 要件定義のコストを負担する気がない、ときです。 (1) が真のとき、当然 (2) も真になります。何が欲しいかを合意することなく、開発が進むと、当然、コレジャナイみたいな展開になるわけです。

もう、こんなことさんざん議論されていることなのですが、未だに、こんなことが起こっているような気がします(ソースは脳内)。SIer に指摘してもらうとか、コンサルしてもらうとか何でもいいけど、発注者はなんらかの形で、要件定義のコストを負担しなければいけないと思っています。伝わったことが伝えたことなのです。要件定義にOKを出すことができるのは、発注者しかいない。ここから次のことが分かります。

1. 発注担当者には要件定義の責任がある
2. 責任を果たさない奴はクソである
3. 私はクソである

たぶん一発で要件定義をすることは難しいでしょう。オンラインのサービスなら、なおさら。であれば、システムの稼働中や開発中に要件定義が変更になる、というのは、組み込んで置かないといけない。と、むりやりアジャイルな話に持って行ってみたり。

完璧なリリースに向けて

「完璧なリリースなどといったものは存在しない。完璧な絶望が存在しないようにね。」

関わっているプロジェクトが一旦落ち着いているように見えて、実はまったくもって佳境なのですが、その中で日々思いついたことをだらだら書き残していこうと思います。

一連のエントリには、便宜上、 Continuous Delivery (継続的デリバリ)のタグをつけます。ですが、 Farley と Humble の著書が扱う範囲とは必ずしも一致していません。アジャイル開発手法的なことも書くでしょうし、もっと細かいテスト手法のことも書くでしょうし、プロジェクトマネジメントとか成果物の納品形式や計画について書くかも知れません。クリックスルーレートが 0.0000% のAmazon へのアフィリエイト集になる可能性も大いにあります。なので、いずれはもっといいタグに変更するでしょう。

自分自信の考えはまったくまとまっていませんし、何が問題なのかさえ把握できていません。経験も交流も少ないので、単に答えを知らないこともあります。もしかしたら、最後には「頑張るしかない」みたいな泣きたくなる結論が出るかも知れません。それでも、まずは書き残す試みをしてみようと思います。

2012-04-26

as3c2dm を使うときに、送信側が使うパラメータ

Adobe AIR で Android アプリを開発できるんですが、そのアプリに C2DM で通知しようとして手間取ったのでメモ。

as3c2dm というライブラリを使うのを想定しています。

結論から言うと、C2DM サーバに送る POST パラメータには、
  • data.parameters
  • data.tickerText
  • data.contentTitle
  • data.contentText
を含める必要があります。

アプリから Google のC2DM のサーバから Registration ID を受け取るときには、ActionScript で onRegisterd() 関数を定義しておきます。通常は、この関数の中でどっかのサーバに Registration ID を登録するような処理をします。

Java レベルでは BroadcastReceiver が、com.google.android.c2dm.intent.REGISTRATION というインテントを受け取ったら、onRegisterd() を呼び出します。

ところが、C2DM サーバからメッセージデータがプッシュされたとき(インテントは com.google.android.c2dm.intent.RECEIVE)の関数定義が、サンプルにはありません。onMessage() みたいなのを呼び出すこともない。で、ソースを見てみると...

public class C2DMBroadcastReceiver extends BroadcastReceiver {
     // ...
     @Override
     public void onReceive(Context context, Intent intent) {
          if (intent.getAction().equals(
            // ...
          } else if (intent.getAction().equals(
                    "com.google.android.c2dm.intent.RECEIVE")) {
               handleMessage(context, intent);
          }
     }
 
     // ...

     private void handleMessage(Context context, Intent intent) {
          try {
               // ...
               String parameters = intent.getStringExtra("parameters");
               CharSequence tickerText = intent.getStringExtra("tickerText");
               CharSequence contentTitle = intent.getStringExtra("contentTitle");
               CharSequence contentText = intent.getStringExtra("contentText");
// ...

待てやコラ ヽ(`Д´)ノ

この後、Notification オブジェクトを作って、端末に表示する処理が入っています。

なんで onRegstered() と同じレイヤで受けられるようになってないのか、唐突に tickerText じゃねーよなどと思ったわけです。が、事情をよく分かっていないのでなんとも。Notification とか Toast ってプラットフォームの機能なので、ActionScript からアクセスできないか、アクセスが大変なのかも知れません。

C2DM を受け取る処理の置き場所

Android のプッシュ通知であるところの C2DM 。この処理をどこに書けばいいのかを調べるのに、えらく時間がかかったのでメモ。

注意: Hello World も書いたことがなくて、クライアントアプリは他の人がやってくれるんだけど、動作確認をしようとした、というシチュエーション。Android やってる人なら、たぶん常識。

Google Developers の Android Cloud to Device Messaging Framework に、必要なことは載っています。困ったのは Handling Registration Results 以下にある onReceive() メソッドをどこに書くのか、ということでした。文章には your application に、としかないし。

このサンプルの場合、マニフェスト XML のなかに
<receiver android:name=".C2DMReceiver" android:permission="com.google.android.c2dm.permission.SEND">
と記述します。なので、メインのアプリ(っていうんですかね? Activity っていうんですかね?)と同じ階層に C2DMReceiver.java を作って、その中に、

public class C2DMReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //...
    }
    //...   
}

みたいに書きます。

2012-04-16

体重と食事は別のブログへ引越し

あまり知られていないけれど、もともとこのブログは Python に関連する技術的なことや、その周辺のことを書くつもりだったのだけれど、最近狂ったように、体重と食事を晒していて、それは、いかんのではないかということで、 体重とかは http://endorphinmania.blogspot.jp/ で続きを書きます。あっちもこっちも、読者ほとんどいないんだけれど気分的に。

2012-04-13

健康診断を受ける

健康診断を受けてきた。ほとんど流れ作業で、細かいこと、たとえばガンの兆候なんかは分からない、と聞いたことがある。けれど「ちょ、こいつ、末期だろ」くらいは分かるだろうと思って、わりと真面目に受けている。

問診票の回答が我ながらダメすぎて泣けてくる。一部抜粋。
  • 自覚症状 → 太ってきた
  • 1日30分以上の汗をかく運動を週2日以上、1年以上実施していますか? → いいえ
  • 日常生活において歩行または同等の身体分銅を1日1時間以上実施していますか?→いいえ
  • ほぼ同じ年齢の同性と比較して歩く速度が速いと思いますか?→いいえ
  • 20歳の時の体重から10kg以上増加していますか?→はい
  • この1年間で体重の増減が±3kg以上ありますか?→はい
  • 夕食後に間食(3食以外の夜食)をとることが週に3回以上ありますか→はい
  • お酒を飲む頻度 → 毎日
  • 運動や食生活の生活習慣を改善してみようと思いますか? → 近いうちに

人体の前投影面積

スイム 500m 12分。体重 67.4kg、体脂肪率 23.9% 。25m を19から20ストロークで泳げた。前回は少なくとも20ストローク必要だった。これが進歩であることを願う。上半身を沈めて、前方からみて脚を隠すつもりの姿勢をとっている。実際にどんな姿勢なのかは不明。

先日、直方体の前投影面積を計算して、1度傾いているだけで、10%近く抵抗が増える、と書いた。けれど、この数字は直方体の各辺の長さの比率によって異なる。興味があるのは、自分の身体ではどの程度のロスが出るのか、だ。

計算するにあたって、体の複雑な形状や、体の丸みをここではないと考える。悪かったわね、丸くて。概算を出したいのだから、細かいことはいいのである。私の体がコレジャナイロボみたいな形をしていると仮定する。上から見たときの面積 を B、 正面から見たときの面積を C とすると、泳いでいるときの進行方向からの投影面積 A は以下のようになる。

A = B cosθ + C sinθ

自分の体をなんとなく画像で撮影して、概算をだすと、B = 5200 cm^2、 C = 2500 cm^2 程度だった。式に当てはめてみると、


  • θ=0度 ... 2,500 cm^2
  • θ=1度 ... 2,592 cm^2 (+3.7%)
  • θ=2度 ... 2,683 cm^2 (+7.3%)
2度傾くとかなり影響があるので、やはり水中姿勢は重要だ。ちなみに、上から見た時の画像を見ると、おそろしく腹が出ていたので、痩せれば 5% くらい節約できる気がする。



2012-04-12

3本ローラの振動に悩む

以前、ちゃんとトライアスロンのトレーニングをしようと思ったときに、3本ローラを買った。ペダリングやバランスのとりかたなど、自転車に乗るスキルが身につく、はずだ。負荷装置をつけなくても、そこそこの負荷がかかる、はずである。

問題は騒音と振動だ。意外に音が出る。さらに深刻なのは振動で、結構な勢いで響く。騒音動画を公開している人もいるけれど、聞いた感じ振動による騒音だ。(以下の動画は、知らない人のもの)


音とは空気の振動なので、同じことなのだけれど、なんというか自転車やローラ台が、目で見えるくらいの周波数の振動が原因のように見える。ローラ、ペダル(クランク)、ホイールが回りまくるので、床やローラ台の構造と共振するのだろう。

私の場合は、どうもホイールが共振しているように見える。空気を入れるためのバルブの周期と、一番大きな振動の周期が一致しているっぽい。こんど検証してみよう。

それで、みなさん対策を講じているようだ。ただ、どういう条件で、どういう対策が効くのがいまいち分からない。軽微な対応でうまくいっている人もいるし、厚さ10cm 以上の防振剤を使っている人もいる。

ぐぐって調べつつ、 Facebook でアンケートもとっているが、こういうのは普段からコミュニティに入っているほうが情報を集めやすいと思った。自転車友達がいないので(そもそも友達がいないわけで)どうも情報が集まりにくい。(いかの動画も知らない人の)



2012-04-11

おめざ


通勤ウォーク 2.8km 35分。体重 67.9kg、体脂肪率 24.%。小雨がぱらついてたけれど、大したことなかったので、歩いた。


寝付きが悪い上に、寝起きが悪い。早起きした自分へのごほうびを、おめざ、というらしい。残念ながら、台所に向かうことさえ億劫なので、これまでおめざが通用しなかった。しかし早起きの神が降りてきて、名案を思いつきました。枕元に置けばよい。というわけで、たけのこの里で目覚めた。しばらく続けてみよう。

2012-04-10

直方体の前投影面積

スイム 500m 10分。体重 68.1kg 体脂肪率 21.0% 。20 ストロークで安定して25mを泳げるようになってきた。水中姿勢がよくなってきたからか。


1000mm x 500mm x 200mm の直方体を考える。文庫本が巨大化したような形状だ。これを一定の早さで、水中を移動させるとき、文庫本の向きがどの程度影響するかを考えてみる。

文庫本を倒した状態で、文庫本の底(500mm x 200mm の面)を進行方向にすると、一番抵抗が少ない。流体を進むときの抵抗の大きさは、速度×面積に比例するからだ。文庫本の表紙の面を進行方向にすると、かなり抵抗が大きい。



文庫本を真横に倒しているときが抵抗が少ないわけだけれど、すこし傾いているときはどうだろう。水中で(私のような)ヘタクソが泳いでいると下半身が沈んでいる。文庫本にもこういう姿勢を取らせて、水中を進む場合を考える。このとき、水の抵抗を受ける前投影面積 A は次の式で得られる。

A = 500 x (200 cosθ + 1000 sin θ) [平方ミリメートル]

θ(シータと読む)は傾いている角度だ。これを計算すると、


  • θ=0.0° ... 100,000 [平方ミリメートル]
  • θ=0.5° ... 104,359 [〃]
  • θ=1.0° ... 108,711 [〃]
となる。体が1度傾いているだけで、8.7%も抵抗が大きくなるのだ。なかなか進まないわけである。バルス。




2012-04-09

トレーニングのケア不足を指摘される

トレーニングなし。体重 67.7kg、体脂肪率24%。



整体の費用対効果に疑問を持ったわけだけれど、ランの後で、整体に行ったら「あれ?走ったりしました?」と聞かれた。はい、と答えると、ケアができていないので、いつもよりかなりカタいと言われた。仕事がしんどいから整体に行っているのだ、的なことを書いたけれど、トレーニングもかなりな勢いで影響していたらしい。せめてというわけで、ストレッチをした。

エンデュランススポーツの応援テクノロジ

ラン 4.5km 30分。整体師の心配と、先週の膝の痛みを考慮して、控えめに走る。さぼる言い訳はいくつもある。

一般選手のエンデュランスイベントにおいて、応援に意味があるのか甚だ疑問だ。スタートラインに立った時点で、市民ランナの結果はほぼ決まっている。応援があってもなくても、たぶん結果は変わらない。とは言え、行きたいので行く。荷物持ち、直前まで着ていた上着を預かる、水を持ってくる、ビールを買っておく、うっかり飲んでしまうなど、レースの前後にアシスタントになることには付加価値がある。

けれど、テクノロジがこれを変え始めた。と、ここで、大きく出てみよう。

ニューヨークシティマラソンで、アシックスがやった(金を出した)システムがある。応援する人が、事前に応援したい人当てにメッセージを登録する。選手のシューズには計時用RFIDがついていて、アンテナがあるところを通過したら、誰がいつそこを通過したか分かる。そこにスクリーンがあって、各選手宛の応援メッセージが液晶パネルに出る。

フルマラソンの、しかも人気大会の応援はすごく大変だ。だいたい、何時間もコース近辺にいるとか、時間効率が悪すぎる。上の応援システムがあれば、手軽に応援を届けることができる。

東京マラソンも各選手の5kmごとの通過タイムが、ほぼリアルタイムでネットで公開される。八重洲でビールを飲んで暖をとりつつ、15km通過したみたいだから表に出よう、みたいなことができる。

RunKeeper の有料会員になると、GPSで取得した現在地をひたすらウェブにアップロードするオプションが使えるらしい。Google latitude でも、友達を探すでもいいんだけど、エンデュランス系スポーツで現在地を公開するというのは、応援をしやすくする。

あと、走りながら Twitter とか、原始的だけれど楽しい。30knから本気出す、
折り返しでもいってたやん!みたいな。そういうコミュニケーションがなくてもマラソンは楽しい。ないほうが楽しいときや、そういう人もいる。けれどコミュニケーションがあると、より楽しい場合がある。応援というより、実況と突っ込みみたいなコミュニケーション。飽きたらやめるし、見なければいいっていう気楽さ。今年のレースでやってみたい。

などということを、ハーフマラソンの周回コースの傍らで考えた。あ、もうすぐゴールのようです。



プレゼンとマジック - 森博嗣「タカイ×タカイ」

森博嗣の X シリーズ 3 作目「タカイ×タカイ」の文庫が出ていたので、読んだ。

マジックって、たしかに、観客に何かが起こっていると見せかけるけれど、それって、実際に起こっていることを隠すためのものですよね。

真鍋瞬市が、高いポールの上に死体があったことに対して、死体を見せたいからではなくて、別のことから目を逸らしたいからでは、と、疑問を投げるセリフだ。実際がどうたったかは、読んでのお楽しみ。



ここから我田引水。プレゼンをするときにも、この手法は使えるだろう仮説。少なくとも私は今まで使ってきたし、これからも使うと思う。

場合によるけれ聴衆に何かアクションを起こして欲しいとき、何か変化を起こさせたいとき、伝える側がフォーカスして欲しいことに、聞き手にフォーカスさせたいことがある。そんなとき、嘘じゃないけど、注意の引き方を工夫する。

たとえば新製品や新サービスを発表をするとき。当然だけど、競合できて、自社製品にできないことや劣っていることがある。おそらくそれは戦略上、優先順位が低いから落とした機能だろう。けど、状況によっては(メディアや顧客は大事だと思い込んでいるかも知れないけど、こちらは重要ではないと思っているとか)、めんどくさい形で突っ込まれるかも知れない。そんなときには、100項目もある新機能ぜんぶではなくて、ポイントになる数機能にフォーカスさせるようなストーリィの組み立て方をする。うまくいっているのか、うまくいってないのか分からない。

ところで、そうやって推理小説にうまく騙されたときは気持ちいい。


2012-04-08

サドルの前にずり落ちる

バイク 18 km、59 分。先週と同じコースを軽く走る。先週に比べて楽だったのは、体が慣れてきたからか、単に風が弱かったからか。



バイクのハンドルとサドルのポジションを半年前に出たレース以来ほったらかしである。直線で DH バーを使うのが前提の前乗りポジション。今は、フィットネスレベルがガタ落ちだし、腹も出ているので、こんなポジションで乗ったりしない。ドロップハンドルのブレーキレバーを握っている。すると、どうも尻が前にずり落ちてきて、しんどい。次に走るときには、サドルを少しさげてみよう。あと、サドルがちょっと低い気もする。


2012-04-07

整体の費用対効果

ラン 6.0km 38分。体重 68.1kg、体脂肪率 20.3%。 5kmくらいで膝が痛くなって、フォームが崩れてくる。しばらくは、4km くらいで体を慣らすのがいいかも知れない。


土曜日には整体に行っている。首、肩、腰のどこかがものすごく凝っていたり、痛かったりするので、週に1度ケアをしている。短い時で60分、長い時で120分。間違いなく仕事の疲労なのだけれど、費用対効果は実際のところどうなんだろう。もっと楽な仕事について、給料の下がり幅が、整体の時間と費用よりも小さいなら、そのほうがいい。実際に試すわけにいかないのが悩ましい。

とりあえず10分だけやる


Getting Started Is Everything という記事の内容を実践してみたら、簡単なわりに効果があった。やる気が出ないときに10分だけやる、というシンプルな方法。

... I convince myself to spend ten minutes on my project. Just ten minutes. Enough time to accomplish one small task. Then, after that ten minutes is up, I can go back to zombies, guilt-free, if I so choose. 
The beautiful thing is, I almost never do.
10分だけやってみようと自分に言い聞かせる。たった10分だ。ちょっとしたタスクなら完了するだろう。10分たてば、罪悪感を感じることなく、ゾンビ映画でも見ればいい。 
ポイントは、ほとんどの場合、ゾンビ映画を見たりしないってことだ。

とりあえず10分やることで、なにがしかの進捗がある。記事に書かれているとおり、たいてい 10 分でやめないので、プロジェクトはもっと進む。普段仕事に集中するために、3-2-1 という OS X 用のアプリを使って、いくつかタイマを用意している。ポモドーロ用に25分と5分、とりあえず片付けるための2分、そして、とりあえず取りかかるための10分。


10分のタイマだけは設定の「Bring to front」のチェックを外している。こうしておくと、10分たったときに、まだ集中して作業をしていれば、そのまま続けられる。


とりあえず始める、というのは、すぐやる系の自己啓発本にも書いてあったのだけれど、10分でいいんだ、と思うと気が楽になって、先延ばし屋の私もとりかかれるようになった。おかげで、手付かずのタスクがほったらかしになるのが少なくなった。