ラベル Development の投稿を表示しています。 すべての投稿を表示
ラベル Development の投稿を表示しています。 すべての投稿を表示

2016-07-07

Monkey-C とかいうプログラミング言語

Garmin の GPS ウォッチのうち上位モデルは、Connect IQ というアプリをインストールするしくみがある。その実行環境を指すのか、アプリ群を指すのかいまいち分からないんだけど、とにかくそういうことができる。

アプリの開発には Monkey C なる言語を使う。なんだよ、それ。ざっとググッても、そんな処理系は見つからない。

using Toybox.Application as App;

class MyProjectApp extends App.AppBase {
    var mymember = 1.0;

    function onStart() {
       mymember = 20.3;
    }

    ....
}

Java に、型推論するための構文糖衣をかぶせたように見える。もしかしたら C# に近いのか。duck type するとか書いてある。

こんな市場の小さな実行環境のために、わざわざ作るのか?

もしかしたら、このデバイスのストップウォッチや、GPS の記録や、加速度センサーを使ったランニング計測なんかの、もともと入っている機能もこの言語で書かれているのかも知れない。だとしたら、ちょっとした処理系あるいはプリプロセッサーがあってもよさそうではある。

いまのところ、ちょっと動作の仕方を見ているだけなので、大して複雑なことを書くことはない。どっちかというと、フレームワークやデバイスのAPIで何ができるかのほうが、ややこしい。しばらくは getter と setter くらいでやっていけるだろう。

2016-07-05

デバッグの手伝い

最近、デバッグの仕事を頼まれた。デバッグというか、不具合/パフォーマンスの問題があるコードまたはインフラの修正を手伝って欲しい、と。具体的にどんな作業になるのか分からなかったので、現地でいろいろ見るという流れだった。

とは言え、それほど技術力があるわけではない。提供できる数すくない武器は、第三者の視点と、開発者とは異なる経験がちょっとある、という程度だ。それらを最大限に使わねばなるまい。

具体的にやったことは、話を聞いて、一番厄介そうなことの原因候補を挙げて、順番に確認していきましょう、と言っただけだ。開発者が、ここじゃないだろうと思っていた、ごく小さなところを、無邪気に確認したら、突破口があった。

と、謙遜めに書いたけれど、そういう状況での問題のまとめ方、切り出し方、アプローチの仕方に価値があるのだろう。

2015-12-13

多重度・本番・流星 〜 テレビ連動ウェブサービスの勘所

欲しいものを手に入れたり、何かを成し遂げるための心構えや方法論というのがあって、自己啓発書やセミナーで紹介される。何年も前に参加したイベントが、ちょっと意識高めで、そこでも紹介されていた。

「流星理論というのがあってだな」
「何すか、それ?」
「流れ星を見たら、願い事を3回となえるんだ。すると願いが叶う」
「ちょw」
「笑ったね?流れ星が突然見えたとき、とっさに3回も言えるってことは、常にそのことを考えてるってことだよ」

いま考えていることは、成果になる可能性がある。であるならば、考えていることが、誰かに某かの足しなるかも知れない。このところ、テレビ番組と連動するウェブサービスの、要件定義について考えている。

以下は、これまでの経験で、おおざっぱに個人的に認識している課題と解決策の案だ。特定の放送局、制作会社、番組の話ではない。

個数が決まらない


「Facebook Messenger に、いいねスタンプあるじゃないすか」
「ありますね」
「いつからか、長押しすると巨大化するようになりましたね」

あるオブジェクトに関連する、別のオブジェクトの個数のカテゴリーとして、0個か1個、必ず1個、0個以上、1個以上などがある。特に「必ず1個」というのは設計上/実装上、強めの制約条件になるので、チューニングに活用できる。

業務系のアプリケーションでは、目的的に多重度の決定がなされる気がする。顧客管理アプリケーションにおいて、顧客の電話番号をいくつ登録できるのかは、そのアプリケーションで解決したい問題と深く連動しそうだ。

たとえば「ここは Facebook のいいね!と同じ」と言われ、問題ドメインにおけるモデルも Facebook のいいね、と同じだと考えてたとしよう。2週間後、連打でいいねのアイコンが大きくなる、いいね数がポイントとして与えられる、しかも、時間がたったらポイントが消えていく、といったクリエイティブなアイデアが浮上する。

この種の要件は発見的に決まりがちだ。やってみたら、面白かった的な演出要素である。もちろん、演出は問題を解決する手段だけれど、顧客名簿の電話番号と違い、いいねだけを切り出して要件定義をしにくいため、プロセスの構造が少し違う。関わったことはないのだけれど、ゲームも同じような構造がありそうな気がする。

コードシンプリシティでは「未来を予測せずに、未来に備える」ことを提案している。ちょっとした切れ目を入れておくのだ。API が返すデータと、データベース上のデータをガチガチに対応づけないようにする。いいねの数は、内部で get_likes() 関数を経由して取得するとか、getter/setter にしておくとか、そういう基本的なことだ。


Rails のアクティブレコードは楽ちんだけれど、この手の届く距離にデータを置いておくアプローチだけでは、変更対応がしにくくなるのだ。



稼働中の本番環境でテスト


「もう1回」
「だめですよ。もう勝負決まりましたよ」
「さっきのは練習。次が本番」

稼働中の本番サーバーで、副作用のあるリハーサルを要求されることがある。何を言っているか分からないと思うが、私には分かる。本番環境を使うことの意味が、番組制作とウェブサービス開発では異なるのだ。

テレビ局のスタジオから生放送することを考えよう。予算とスケジュールが許せば、本番とまったく同じ物理リソースで、事前にリハーサルができる。セット、カメラ、音響、照明すべてに、本番と同じリソースを使うことは物理的に可能だ。難しいのは、出演者のスケジュールの都合で遅れたり、トイレが我慢できなかったりで、時間がずれるとかの対処だろうか。

スタジオに併設されているサブ・コントロール・ルームでは、本番時もリハーサル時も各カメラからの映像(と、必要であれば録画した映像)がたくさんのモニタに映し出され、ディレクターがどの映像を使うかを指示/選択できる。サブ・コントロール・ルームの出力は、マスター・コントロール・ルームで送信所(東京の地上波ならスカイツリーとか)に出すように切り替えない限り、視聴者が知覚できる副作用は発生しない。

ここに番組宣伝なるものが、カジュアルに提案される。もちろん、いくら宣伝をしたとしても、番組コンテンツの視聴という副作用は発生しない。

ところが「番組宣伝期間中、事前登録をしたユーザーに10ポイントあげる」となると、ユーザーとインタラクションが発生したウェブサービスに、外部から知覚できる副作用が発生する。やっかいである。

リハーサルに開発サーバーを使うと「俺、事前登録したのに、10ポイント入ってないけど」という声があがるのは楽なほうだ。2夜連続放送の2日目のリハーサルで「昨日ゲットしはたはずの牛丼クーポンがなくなってるぞ。大丈夫なの?」とかなるとアレだ。

唐突に聞かされるとやっかいだけれど、事前に分かっていれば対応可能な要件ではある。ひとつの解決策は、サービスの機能として、本番モードとリハーサルモードを用意しておくというものだ。

本番/リハーサルでAPIに渡すパラメータを変更する、リハーサルモードでは過去の本番データを参照するが、未来のデータはリハーサル用の領域に書き込むなどの機能を実装すればよい。あるいはデータが小さければ、スナップショットをとってリストアする処理を、しょっちゅう行うものとして作ってもよい。

おわりに


以上、最近考えていたことを、書き出した。

今年の年末の仕事を請けたときには、放送日が確定していなかったので、参加申し込みをしなかった忘年会がある。結局、放送日と忘年会の日程はぶつからず、惜しいことをした。飲み会や、トライアスロン/マラソンレースのキャンセルに気をもむのが嫌になった、というのも前の仕事を辞めた理由のひとつだったことを思い出した。

「カネカネカネ」
「何すか、急に?」
「いや、ちょうどあのへんに、流れ星…」 


※ 縛り
・題名:「星」を含むこと。(例:「星の王子さま」「きらきら星」など)
・書き出し:書き出しをひらがなにしたとき「ほし」と表記できること。(例:「星空」「干し芋」「保守」など)
・文中に以下の3つの縛りワードを含むこと。
  縛りワード1:「この手の届く距離」
  縛りワード2:「我慢できなかった」
  縛りワード3:「牛」
・結び:「星」で終わる。ただし、句点(。)、疑問符(?)、三点リーダ(…)、その他の文章記号をつけてもOK。

2015-11-22

オブジェクト指向UX、というアプローチ

オブジェクト指向UXという記事を見つけた。原文は Object-Oriented UX 、和訳がオブジェクト指向UX である。

この記事では UXデザインのときに、まず最初に、ユーザーのメンタルモデルを、オブジェクト指向的に分析・定義することを、提案してる。このアプローチを、OOUXアプローチと呼ぼう。

オブジェクト指向やUXの意味は、人によって地味に異なるので、OOUX という言葉は誤解を生むのかも知れない。

要件(と呼ばれるウィッシュリスト)から、ソフトウェアの仕様を定義する立場で仕事を請けているんだけれど、このアプローチは有効だと思った。

[エンジニアは]ワイヤフレームやプロトタイプを見た時に、デザインをリバースエンジニアリングしてオブジェクトを抽出します。そして彼らは、「オブジェクトXはオブジェクトYとどのように通信するのか。オブジェクトAは複数のオブジェクトBで構成されていのか。オブジェクトの属性は何か。このクラスのオブジェクトはあのクラスのオブジェクトを継承するのか。」と思うでしょう。
ユーザのメンタルモデルを模倣するオブジェクトを定義することで、チームのコミュニケーションの土台となります。共通言語を手に入れられるのです。

ソフトウェアが対象にするものが、技術的なレイヤーから見て高ければ高いほど、この種の作業は必要になる。OOUX は、このための共通言語の表記の仕方とアプローチの提案なのだ。

脱 UML


似たようなアプローチとして、ドメイン駆動設計がある。オブジェクト指向的に問題ドメインをとらえることで、要件が分かるしコミュニケーションとれる、という考え方だ。ドメイン駆動設計では UML っぽい図をつかって表記する。これは悪くはないんだけれど、うまく伝わらない感がある。記号にルールがあって説明が必要になるし、後述するけど multiplicity がうまくイメージしてもらえないときがある。

OOUX のアプローチ


手順としては、まず要件のリストのようなものから、オブジェクトを抽出する。たいていは名詞がオブジェクトになるんだけれど、暗黙に想定しているオブジェクトがあったりする。

次に、オブジェクトの属性を挙げていく。さらに、他のオブジェクトを参照するのであれば、それを書いておく。これらを優先順位付けしろ、話はそれからだ、という手順だ。

メソッドやメッセージのやりとりは、ここでは定義しないようだ。これは欲しいなぁと思うので、今後自分が使うときには、なんか記載するようにするかも知れない。

さて、このとき UML を使わない。


  • レシピ
    • 材料
    • 材料
    • 材用
    • ...
    • 手順

  • 材料
    • 名前
    • 説明


みたいに書く。

レシピには1以上の材料が必要だろうから、1..* とかって矢印を引きたくなるんだけど、そうしない。

本当はUMLとやってることは同じだ。けど、1..* よりも具体的に材料、材料、材料、...と書いておくほうがイメージが湧く。

設計するにあたって、あるいは、いざ実装し始めてから、以下の様な会話をして泣いたことが何度もある。

「ここって複数なんですか?」
「ほとんどは、1つしかないよ」
「3つっていうのは絶対にない?」
「たまにある」
「それは表示する必要がある?」
「もちろん」
「100個の場合も?」
「まあね。でも、ほとんどは1つだから、気にしなくていいよ。」

気にしますね。とっても、気にします。たとえ1ケースであっても、99999オブジェクトを参照する可能性があるなら、ちと考えたくなるものです。

SNS を使うようなのは、これがしょっちゅうある。ほとんどのユーザーのフォロワーは100人くらいで収まるけど、たとえば @takapon_jp には 14万人のフォロワーがいる。

UML の 1..* と書いてあるよりも、


  • レシピ
    • 材料
    • 材料
    • 材料
    • ... 最大1000 ...
    • 手順


のほうが、UMLに馴染みがない人にも、可視化できるというわけだ。

実装がオブジェクトになるかどうかは、また別の話。RDB で管理するとか、KVS とかは、今は別の話。開発の工数や、成果物の効果によって、つまり費用対効果を考えて、10個までに制限するとかいう議論はあるけど、今は、その前の話をしている。

発注側が意識しているかどうか別にして、現時点の想定では上限の無い 1..* が、まずは要件の草稿になっていることが明らかになるのだ。

さっそく使ってみた


マーケットプレース(楽天とかね)みたいなのを考えることがあった。このとき店舗オブジェクトを考える。この店舗にいくつか種類があって、abstract 店舗から、concrete 店舗を4種類派生させるような感じだった。けど、UMLにせずに、

通販店舗、デジタル商材店舗、...みたいにあえて、コピペで書いていった。そうしたら、どうもビジネス主体(会社とか)が、複数の店舗を持つことができて、かつ、ビジネス主体ごとに売上を管理する必要がある、みたいなことが明らかになった。最初のウィッシュリストには、どこにもそんなことは書かれていない。

1年ほど前に、似たような状況でUMLを使って説明し、ほんとにこういうの要らないの?と確認したときた時には、完全にスルーされた。あとから会社単位の売上管理が必要だと言い始める、という、だるい展開だった。

1年前と先週の違いは、図の描き方だけではないので、必ずしもOOUXのアプローチの優位性を示さない。けれど、よさそう、という印象を持った。機会があれば、かつ適切そうであれば、今後もこのアプローチを使っていこうと思う。

OOUX が目指していないであろうこと


原文ページのコメント欄や、SNS での言及をみていると、OOUX のスコープを把握できない人がいるように見える。

前提として、この記事は、同じ種類のコンテンツが大量にあるような、ウェブサイトの見え方/体験の設計という意味で、UX デザインという言葉を使っている。選挙(選挙区ごとに結果が出ますね)、レシピ、DIY とか。この種のウェブサイトのコンテンツでは、クラスとインスタンス、という考え方が比較的あてはめやすい。

20年ほどの昔、オブジェクト指向で要求を分析し、オブジェクト指向で設計し、オブジェクト指向で実装すると、分析・設計・実装で共通のオブジェクトを使える、という夢を見ていたことがありました。が、実際には、そうはいかない。

OOUX もドメイン駆動開発も、メンタルモデルをオブジェクトにマッピングしましょう、という考え方だ。決して、いきなり実装しようとはしていない、と私は解釈している。実装するときのモデルとは大きく異なることがあるだろう。

さらに、単純にマッピングできないこともあると思う。当初は想定さえしていなかったような、暗黙のコンセプトが、オブジェクトとして浮き彫りになることもある。

「それは 0以上なのか、1個なのか、N個なのか?」が自分には分からないが、知らなければならないとき、OOUXで紹介された記述の仕方が使えるのだ。

2015-10-17

iPhone アプリ開発をかじろうとする

iPhone/Android アプリのディレクション仕様策定ともろもろの意思決定みたいなのをしたことはあるのだけれど実際に作ったことはなかっただいたいが通信やドメインモデル的なデータ構造を主に担当することが多かったわけで

そんなわけで iPhone アプリの作り方を知ろうと思い立った。

だいたい2日もすると、やる気がなくなってしまうので、勢いで本を購入。簡単すぎるくらい簡単なところから、と思って、簡単そうな本を買ってやってみた。基本的に書いたとおりに写経すればいいんだけど、うっかり、Xcode 7 をインストールしてしまって、微妙に違う箇所があったりする。



この本、構成はいいんだけど、たとえがイマイチなので、好きではない。例示ではなくて、たとえ。

とはいえ、書いてあるとおりに書くと、動く。その後で解説がある、という構成は、私にとって分かりやすい。

続いて、Apple のサイトにある iOS 9、 Xcode 7 のチュートリアルもやってみた。ふむふむ。

Swift は言語仕様的に、冗長なところがあって、同じことをするために複数の書き方があるように見える。class があれば struct は要らないだろとか、継承、extension、プロトコルが地味に被ってるようなとか、delegate は機能じゃなくてデザインパターンかよとか。

けれど、誰かが「言語の問題ではくて、OS X や iOS アプリの既存フレームワークを使うために、そのようなことになってしまっている」と話している見て、納得した。

アプリ開発を請け負えるかと言われると、まったくもって無理ではあるけれど、デバッグの手伝いくらいは(ピザを注文する以外にも)できるようになったような気はする。



2015-02-15

Coplien、 Harrison「組織パターン」 〜 一部を犠牲にして、全体を進める

スケープゴート


組織パターンという本に「誰か一人を犠牲にする」パターンが紹介されている。

どんなに細かくても余計な作業には対処しなければならない。しかし、第一優先のタスクをやるべき時間がとられてしまうことも忘れてはならない。[...]
細かい余分な作業が大量にあると、やりたい仕事ができなくなるのだ。
それゆえ:
誰か一人余計な作業に割り当てて、その作業を終わらせてもらおう。

犠牲とか一人とか言い回しは、おそらく意図的だろう。犠牲になった人の作業が終わったら、元の仕事に戻してあげようとか書いてある。

ここでは、機械的に話を進める。もうすこし抽象化して捉えて、メインのプロジェクトから、派生したプロジェクトを分離し、一時的にリソースを配分する、という風に考える。ひとつのリソースを複数のプロジェクトに割り当てると、たしかに効率が悪いことがある。

余計な仕事が発生


本を閉じると、そこには現実が待っている。ちょうど数ヶ月スパンでプロダクトを開発しているところだ。プロダクトは機能の多さよりも、キャパシティのほうが重要である、という種類ものだ。運用が始まったとき、機能には妥協ができるが、トラフィックに対するキャパシティは妥協やコントロールがしにくいからだ。

現時点で手元には、

  • そこそこのキャパシティで稼働する、機能 A
  • 開発中の機能 B

がある。機能AやBというのは、実際には複数の機能群だ。フルキャパシティで動く部分もあれば、要件半分くらいでしか動かないだろうという部分もある。だが話を単純化しておこう。

このプロダクトのデモを見たい、という仕事が入ってきた。デモという言葉は曖昧なので、よく聞いてみると「プロダクトの機能 A, B, Cを利用したアプリケーション開発と運用を、実験として行う。運用フェーズを想定して、複数のサードパーティが、アプリケーションを開発する」というものだ。

突っ込みたいところや、だるいところがあり、その過程で感情的になり、箸が転がってもブチ切れるという精神状態になったりもしているが、ここでは機械的には話を進めよう。

不足しているものを列挙すると、以下の3つだ

  • 機能 B(作りかけ)
  • 機能 C(次のフェーズで開発する)
  • サードパーティへのサポート(文書、トラブルシュート)
  • アプリケーションの参照実装

である。問題は機能BとCである。機能Bはキャパを考慮して実装中なので、キャパの低い実装に切り替えると、トータルの手戻りが大きい。C は次のフェーズで開発する機能だ。

  • アプリ
  • プロダクト機能 A  + 機能 B と C を追加

えらく変更/追加が多いではないか。急に入ってきた仕事は「プロダクトができること」を見せるものだ。本番運用があるわけでもない。やっつけ仕事だ。けれど、プラットフォームを使う必要がある。

汚れ仕事


とうわけで、汚れ仕事を分離することにした。プロダクトは予定通り開発をすすめる。この仕事用に、別の使い捨てのコンポーネントを用意する。

  • アプリ
  • やっつけラッパー (B + C)
  • プロダクト (A)

アプリから見ると、A, B, C を提供するプラットフォームが存在する。A を提供するプラットフォームは、きちんと存在していて、ラッパーがプロキシする。

機能B と C はやっつけラッパーの中で、やっつけ実装する。

こうやってプラットフォームの開発を、(ほとんど)遅らせることなく、途中で入ってきた依存関係のあるプロジェクトを進めている。あと2週間くらいは、アルコールの摂取量が、高めになるだろうけど、酒を飲む程度の余裕があるということだ。


2014-05-08

ベストプラクティス()とコンテクスト

第一四半期バタバタしていた。テレビの収録、リハーサル、放送の日程は残念ながら given な条件なので変えられないのだけれど、そうじゃないときは休みを取りやすい。代休も、もちろんちゃんと取れる。部署の上司が取らせようとしてくれるのと、周りが寛容だからだろう。自分ばっかり休んでる気がするけど、気づかないふりして、代休で5日ほど休んでいる。

休んでいると、いろいろ余計なことを見るし、考える。以下は、その考えの吐露である。

きっかけはTDDである。が、TDDの話はいい。よくわからないことはあるし、テストの重要性は分かっていつつも、スキルがー、とかでまだあるべき姿に追いついていないところだ。

そこで、あるべき姿とは何か、である。かーらーの、問題の発見/定義と解決である。たとえば(さまざまな紆余曲折を経て)ちょっとしたDBがあり、Webブラウザで閲覧/更新をしたいとしよう。解決策として Django の管理画面を使うことになったとしよう。たぶんテストファーストでユニットテストを書く必要はないだろう。フレームワークのデフォルト機能使うだけなのだから、そんなものテストする工数のほうが多くなりそうだ。

一方で、そのDBに外部からアクセスできるようにし、ロールごとに細かく権限が異なるようなAPIとして提供し、課金までするとなると話が一気に変わる。おそらくは、リリース後に「ACL間違ってました、てへ」は通用しないだろうから、テストファーストかどうか知らないが、何らかの形で自動テストをしたくなるだろう。

この2つは極端だしわかりやすいんだけど、製品やサービスの機能要件、非機能要件、組織のあり方、開発サイクルなどによって、テストの位置づけは全く違うと思う。コンテクスト抜きで問題定義できないし、解決策候補の評価もできない。

ちょっと長いけれど、引用する。


たとえば、エクストリームプログラミング(XP)の方法論でよく知られている
格言のひとつに、「失敗する可能性のあるものすべてをテストせよ」があります。 初心者にとっては、これはレシピとなってしまいます。初心者は何をテストすればよいのでしょうか?オブジェクトの属性値を設定する「セッターメソッド」と属性値を取得する「ゲッターメソッド」のすべてをテストすればよいのでしょうか?プリント文をテストすればよいのでしょうか?見当違いなものをテストするのが関の山でしょう。
熟練者なら、何か失敗するか ― 正確にいえば、何か失敗する可能性が高いか ― を知っています。経験と判断力を備えているので、この格言が特定の状況(コンテキスト)において何を意味するかわかっています。

(Hunt「リファクタリング・ウェットウェア」)

あなたが置かれている状況と、私が置かれている状況が異なり、コンテクストが違うなら、TDD の意味が違う。TDDの解釈に揺れがない、としてもだ。テスト、devops の意味や位置づけもだろう。なんなら、「開発」とか「製品」とかの意味だって違う。「いやいや、テストとは〇〇だ」と言いたいかも知れないけれど、全く残念なことに、そんなことはない。比較的近いと思っている仕事の人達と話しても、違うことあるんじゃないすか?そんなことないすか? そんなことないと、ここまでドヤ顔で書いてきた私は、ちょっと困るんですけどね。

というわけで、もうちょっと前提条件やコンテクスト明らかにしたほうが、意味のある議論や意見交換ができるんじゃないかな、と思う。私は自分のコンテクストを鑑みて、次のビールを飲む。



2014-04-23

Team Geek を読んで、実践し始めたこと


Fitzpatrik と Collins-Sussman「Team Geek」を読んだ。体系化された方法論として書かれているわけではないので、内容をそのまま紹介することが難しい。この本で問題であると指摘されていることのうち、自分がやってきたこと、どう対処しようとしているかを書いておく。

※ 誤字脱字、分かりにくい表現、スタイルをリライト。ロジックはそのまま。-- 2014/04/24 8時半ごろ

で、でたーw 天才じゃないのがばれるのが嫌だから、途中の成果を隠奴〜w


Most programmers are afraid to share work they've just started, because it means peers will see the mistakes and know the author of the code is not a genius.

これは自分に当てはまる。そのとおりだ。だって天才じゃないって知られるの嫌じゃん。(うすっぺらい)プライドの高さと、器の小ささにかけては、世界トップレベルの自称天才にとって苦痛でしかない。

一方で、ソフトウェア開発における成果にフォーカスするとき、障害になりうる考え方だ。私の場合、確実に障害になる。妥当性のチェックを自分ひとりでできないため、間違いが組み込まれることが必至である。コードレビューでは遅すぎることもある。要件定義のミスがテストまで残ってると、修正コストが跳ね上がるという有名な話だ。つまり、早い段階から、レビューしてもらうのもっともコストが低いのに、その手段を放棄しているということだ。

もうひとつ。自分しか分からないコードやプロセスが存在すると、自分が SPOF (Single Point of Failure; 単一障害点)になってしまう。よっぽど職場で立場が危うくて、どうしても仕事を守る必要があるなら別だけど。

最近は、プロジェクトの初期段階で自分がやろうとしていること、やっていることを共有するようにしている。


で、でたーw 謙虚・尊敬・信頼の欠如でギスギスす奴〜ww


Almost every social conflict can ultimately be traced back to a lack of humility, respect, or  trust.

尊敬というか、敬意を払うってことだと解釈している。

かならず最後に自分のコメントを付け足したがる病についても、触れられている。見ててだるいし、無理に何か言おうとするから、みんながす指摘してないけど瑣末なこと言ったりとかになりがち。つまり、なんの付加価値もない音声のやりとりを待つ時間が増えるということだ。気をつけよう。最近、多い気がする。

以前から気をつけているのは「バグを憎んで人を憎まず」である。悪意や極端な怠慢でないかぎり、バグについて人を責めたりしない。ひとつは、自分が責められたくないから。もうひとつは、さっさとバグを修正することに集中して欲しいからだ。


で、出ーw べき論だけ語って、イライラ奴〜www


If you focus on the way thing should be in your organization, you'll find nothing but frustration and disappointment.

現状把握、問題解決、具体的なアクションを考えろ、ということなのだ。

そして自己アピールや昇進は悪ではないことを、受け入れるときなのかも知れない。組織上の上位のポジションであるが故に実現しやすいこと、というのは確実に存在する。発言力だって大きくなる。もちろん責任は増えるだろうし、非合理的な人間の相手をする必要もある。

ソフトウェア開発者にはひとつ有利な点がある。自動化や成果の複製が容易なのだ。やったことを/作ったことを複製するというオプションが存在する。システム化してしまおう(Excel のフォームを綺麗にするんじゃなくて、マクロで自動送信するとか)、部署間での口頭での調整にしようというような、意思決定と運用は、広い意味でエンジニアリングだ。

数年前、マーケティングをやっていたとき「儲かるしくみをエンジニアリングする」というのが部署の役割だった。要件定義、開発、運用をふくめて回るようにする。そのための広告、ウェブサイト、プレゼン、他部署との調整、値付けだった。

エンジニアリングを実践する技能は、きっと組織の成果を向上させることにも使えるだろうな、と思い始めている。




2011-12-04

mock.patch をデコレータ、コンテクストマネージャとして使う

patch はコンテクストマネージャでもあるので、with を使った書き方ができます。with でつくられたブロックの中でだけ、モックが機能するようになります。
>>> from mock import patch
>>> with patch('random.random') as m:
...     import random
...     random.random()  # もうモックになっている。戻り値は Mock オブジェクト
...     m.return_value = 0.5
...     random.random()  # 戻り値は 0.5
... 
<mock.Mock object at 0x423ed0>
0.5
>>> random.random()  # モノホンの random 関数
0.84432022018162511
with ブロックの中に入った時点で、random.random はモックになってしまいます。必須ではありませんが、 as m のように書いて、変数 m でモックへアクセスできます。patch().start() の戻り値と同じです。
with ブロックを抜けるときに、パッチャー の stop() メソッドが実行されたのと同じ状態になり、モックではなくなります。
複数のモックを使いたいときには、with ブロックをネストするわけですが、それはちょっと鬱陶しいですよね。インデントが深くなりすぎるかも、なので。そんな時は 標準ライブラリの contextlib.nested を使います。
>>> from contextlib import nested
>>> from mock import patch
>>> from __future__ import with_statement
>>> with nested(patch('random.random'), patch('random.randint')) as (m, n):
...     m.return_value = 0.5
...     n.return_value = 3  # random.randint のモック
patch は、デコレータとしても使えます。
>>> from mock import patch
>>> @patch('random.random') 
... def func(m):
...     import random
...     print random.random()
...     m.return_value = 0.5
...     print random.random()
... 
>>> func()
<mock.Mock object at 0x429730>
0.5
>>> random.random()
0.94254256426687633
関数を patch でデコレートすると、元の関数にMock オブジェクトが渡されるようになります。上の例では、第1引数 m が random.random に対応するモックです。このモックが使われているスコープ内、つまりこの関数内でモックが有効になっています。関数を抜けると(正確には、関数を抜けて、デコレートしている外側のスコープを抜けると)、モックではなくなります。したがって、 func() を呼び出した後で、random.random() を呼び出すと本来の戻り値になります。
unittest モジュールを使ったテストを書く時には、テスト関数に、デコレータからモックを受け取るように書きます。
class MyTest(TestCase):
 @patch('random.random')
 def test1(self, m):
  import random
  m.return_value = 10.0
  self.assertEqual(random.random(), 10.0)
というわけで mock オブジェクトの機能でした。機能を知ることと、使えることはまた別なので、実際にテストでどうするのがいいか、は、別の機会に。私自身、試行錯誤しております。

2011-11-28

mock.patch


mock ライブラリの patch 関数の挙動を書きます。

パッチャーの start()/stop() メソッドを使う

標準ライブラリの random.random 関数を例にとります。

>>> import random
>>> random.random()
0.90675850364670885
>>> random.random()
0.9838226858480108


patch 関数を実行してみましょう。

>>> from mock import patch
>>> p = patch('random.random')
>>> random.random()
0.27552766919082217

とくに、何も変わったことは起こりません。patch 関数の戻り値は _patch オブジェクトです。ドキュメントにはパッチャーと書いてあります。パッチャーの start() メソッドを呼ぶと変化が起こります。

>>> m = p.start()
>>> m
<mock.Mock object at 0x366f30>
>>> random.random
<mock.Mock object at 0x366f30>

random.random という名前の参照先が、元の乱数生成関数ではなく、Mock オブジェクトに置き換わっています。random.random と p.start() の戻り値は同一の Mock オブジェクトです。

random.random は Mock オブジェクトなので戻り値を書き換えることもできます。

>>> random.random() >>> m.return_value = 100 >>> random.random() 100 >>> random.random.return_value = 0 >>> m() 0 >>> random.random() 0

パッチャーには stop() メソッドがあり、これを実行すると元に戻ります。

>>> p.stop()
>>> random.random
<built-in method random of Random object at 0x6b3b6210>
>>> random.random()
0.029689653478273681

テストで使う

def foo(x):
    return random.random() * x


関数 foo をテストする場合を考えます。テストすべきは、 

  1. random.random を引数なしで 1 度呼び出したこと。
  2. random.random() の戻り値に、x をかけたものが返ること。
です。random 関数の戻り値がわかっていれば、テストできますね。モックしましょう。

import random
import unittest
import mock

def foo(x):
    return random.random() * x

class MyTestCase(unittest.TestCase):
    def test(self):
        # random.random が常に1を返すようモックる
        p = mock.patch('random.random')
        p.start()
        random.random.return_value = 1
        # テスト対象関数を呼び出す
        result = foo(2)
        # random.random() を1度呼んでいることを確認
        self.assertEqual(random.random.call_count, 1)
        self.assertEqual(random.random.call_args, ((), {}))
        # 戻り値をテスト
        self.assertEqual(result, 2)
        # モックを戻す
        p.stop()  

if __name__ == '__main__':
    unittest.main()


これでテストできるようになりました。ですが、問題がありまして、テスト中に例外が出ると p.stop() が呼ばれません。random.random はモックのままなので、他のテストが実行されたきに、意図しない結果になることもあります。

というわけで、必ず実行されるように、setUp と tearDown で、パッチャーの start() と stop() 使います。

class MyTestCase(unittest.TestCase):
    def setUp(self):
        # random.random が常に1を返すようモックる
        self.p = mock.patch('random.random')
        self.p.start()
        random.random.return_value = 1

    def tearDown(self):
        # モックを戻す
        self.p.stop()

    def test(self):
        # テスト対象関数を呼び出す
        result = foo(2)
        # random.random() を1度呼んでいることを確認
        self.assertEqual(random.random.call_count, 1)
        self.assertEqual(random.random.call_args, ((), {}))
        # 戻り値をテスト
        self.assertEqual(result, 2)

つづき

patch をデコレータや、コンテクストマネージャとして使えるのですが、それは、また今度。

2011-11-25

mock ライブラリの Mock クラス

mock ライブラリを使ったテストのことを書こうと思ったのだけれど、テストの仕方なんて教えてもらう立場なんだから、いい例を思いつくわけもなく、挫折。

というわけで、mock ライブラリで提供されている機能を、簡単に書きだしていくことにします。mock を使ってテストする具体的な方法は、また今度。

今回は Mock クラスの基本機能です。ちなみにこれだけでは、テストを書くにあたってのメリットは限定的です。

Mock クラスは引数なしで、インスタンス化できます。以後、Mock オブジェクトと呼びます。

>>> from mock import Mock
>>> wozozo = Mock()
>>> wozozo
<mock.Mock object at 0x106fc4390>

Mock オブジェクトにドットでつなげると、別の Mock オブジェクトが取り出せます。

>>> wozozo.foo
<Mock name='mock.foo' id='4412162832'>
>>> wozozo.bar
<Mock name='mock.bar' id='4412163024'>
>>> wozozo.bar
<Mock name='mock.bar' id='4412163024'>

同じプロパティ名を指定すると、同じ Mock オブジェクトが返ってきます。上の例では、wozozo.bar は必ず同じ Mock オブジェクトを参照しています。

もちろん、任意の属性に、任意のオブジェクトを代入できます。

>>> wozozo.baz = 3
>>> wozozo.baz
3

Mock オブジェクトを、関数のように呼び出すこともできます。また、 call_count プロパティは呼び出した回数を返します。

>>> wozozo()
<mock.Mock object at 0x106fc44d0>
>>> wozozo.call_count
1
>>> wozozo()
<mock.Mock object at 0x106fc44d0>
>>> wozozo.call_count
2

Mock オブジェクトなら呼び出せるので、ドットでつないで得られるプロパティも呼び出せます。引数を渡すこともできます。

>>> wozozo.unko(1)
<mock.Mock object at 0x106fc4590>

call_count の他にも呼び出し系のプロパティがあります。call_args は、最後に呼び出されたときの引数を返します。また、call_args_list は呼び出されたときの引数の履歴を返します。

>>> wozozo.unko.call_count
1
>>> wozozo.unko.call_args
((1,), {})
>>> wozozo.unko(1, 'a', foo='hoge')
<mock.Mock object at 0x106fc4590>
>>> wozozo.unko.call_count
2
>>> wozozo.unko.call_args
((1, 'a'), {'foo': 'hoge'})
>>> wozozo.unko.call_args_list
[((1,), {}), ((1, 'a'), {'foo': 'hoge'})]

戻り値を指定することもできます。下の例では、wozozo.unko() で 999 を返すようにしています。

>>> wozozo.unko.return_value = 999
>>> wozozo.unko()
999

任意の関数を割り当てることもできます。

>>> def f(x, y):
...     return x + y
... 
>>> wozozo.unko = f
>>> wozozo.unko(2, 3)
5

てな具合です。

何が嬉しいかと言うと、簡単に何かのフリをさせることができる、ということです。すぐに思いつくのは (1) 生成するのがだるいインスタンスのフリをさせる、 (2) 関数やメソッドのフリをさせる、の2つです。

1つめの例として datetime オブジェクトを受け取って、年の情報だけを使うような関数を考えます。

>>> def next_year(today):
...     return today.year + 1
... 
>>> next_year(datetime.now())
2012
>>> today = Mock()
>>> today.year = 2000
>>> next_year(today)
2001
datetime なら、年月日を直接指定できるので大したことないですが、複雑なプロセスを経て、他のオブジェクトへの参照をしているようなオブジェクトで、かつ、一部のプロパティしか使わないのであれば、 Mock オブジェクトで代替すると便利です。

2つめの例としては random 関数を考えます。戻り値が決定論的に定まらないので、テストするのが大変です。そこで random の名前のくせに、常に0.5を返すようにしてみます。

>>> random = Mock()
>>> random.random.return_value = 0.5
>>> random.random()
0.5
>>> random.random()
0.5
>>> random.random()
0.5

こうすると、テストがやりやすくなりますね。

問題は、何かの関数の「中で」random 関数が呼び出される、ということです。もちろん、random 関数を引数に与えるような設計にするといいのでしょうが、人からもらったコードはそうなってないこともあるでしょう。

それを、うまくやってくれるのが mock.patch() 関数です。

眠い。寝よ寝よ。つづきは、またこんど。

2011-11-24

Python 温泉で開発プロセスの教えを乞う


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

前提は、
- 自社サービス開発
- エンジニアの人数は不足気味
- 使用する言語は Python のみ
- ウェブ API 開発がメイン

環境の構築
開発環境を簡単に作れるというのは実はとても重要なファクターです。
これを目指すのがオススメです。git clone | hg clone して make だけたたけばあとは全部用意してくれるが理想ですね。

これは私が苦手なこと(そういうものが私には多い)のひとつです。というわけで、本当に基本的なことだけやりました。

人生初の buildout を使いました。いままで、便利そうなんだけど、よく分かんないってことで敬遠していました。が、今回は 「Python ライブラリを入れる」ことだけを書きました。

Makefile
.PHONY: env

env:
     python2.7 bootstrap.py --distribute
     bin/buildout

buildout.cfg
[buildout]
parts = env

[env]
recipe = zc.recipe.egg
eggs =
    nose==1.1.2
    mechanize==0.2.5
    simplejson==2.2.1
interpreter = python


これだけ。これで hg clone して make したら環境をつくれます。なんで今までやらなかったんだろう。無用な混乱を避けるためにバージョン番号を指定してしてあります。
単純だけど動く buildout.cfg を書けるようになったことが、今回のツール知識の中で最大の成果です。

機能テスト


上の環境は、開発しているアプリケーション本体ではなくて、その機能をテストするためのものです。@voluntas のブログでいうところの外部テストです。なので、nose が入ってるんですね。アプリケーションは HTTP で JSON を返すので、戻り値のチェックのために simplejson を入れています。
import urllib
import simplejson as json

def setup():
    # いろいろ初期化
    __SERVER = …
    ...

def test_my_api():
    res = app.call_api('/my/api', x=1, y=2)
    assert res['status'] == 0

def call_api(path, **kw):
    """API を呼び出して、レスポンスを辞書で返す"""
    params = urllib.urlencode(kw)
    fin = urllib.urlopen('%s%s' % (__SERVER, path), params)
    body = fin.read()
    return = json.loads(body)
上のようなファイルを作っておいて、 bin/nosetest を実行すると、setup して test_my_api を実行してくれます。 nose を使うのも初めてですが簡単でした。

自社ライブラリ


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

次の課題


buildout でインストールした mechanize は OAuth 機能のテストに使います。リダイレクトやブラウザ上で操作などがあるので、自動テストにはブラウザを抽象化してくれるライブラリが必要でした。

def signup_with_twitter(screen_name, password):
    """Twitter アカウントでログイン"""
    def userop(browser):
        # Twitter にログインして承認
        browser.select_form(nr=0)
        browser["session[username_or_email]"] = screen_name
        browser["session[password]"] = password
        browser.submit()
    return _oauth('twitter', userop)

def _oauth(authority, userop):
    browser = mechanize.Browser()
    browser.set_handle_robots(False)
    # OAuth 開始の URL を開く
    browser.open("%s/oauth?authority=%s" % (__SERVER, authority))
    # ユーザ操作
    userop(browser)
    # レスポンスパラメータを取得
    # example.com/path?foo=1&bar=x => {"foo":["1"], "bar":["x"]}
    url = browser.geturl()
    data = cgi.parse_qs(urlparse.urlparse(url).query)
    return data


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


おわりに


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

おまけ


35歳ごろ(定年だというのに)ソフトウェア開発業界に入りたい、と思うようになりました。このとき、Python Code Reading で発表 → BPStudy に行く → Python 温泉に行く → 顔を知ってもらう → どっかの会社に潜り込む、という戦術を妄想していました。そして、37歳にしてやっと Python 温泉に行けました。順番がずれていますが、自分の人生で計画通りにいったことなどないので、自分にしては上出来です。

2011-10-29

ドキュメント書きを効率化した5つの方法

API の仕様書なるものを書いている過程で、繰り返し作業が多く、いくつかの作業を工夫したのでまとめておきます。前提として、私はコードを一切書かず、依頼先にドキュメントを渡し、成果物としてコードを受け取ります。reST で書いて、Sphinx で HTML をビルドし、Wed-DAV 上のディレクトリに置くことで共有します。

1. 継続ビルド

Sphinx によるドキュメントを継続ビルドしています。omake を使わずに、Sphinx ドキュメントの継続的ビルド に具体的な方法を書きました。明示的なビルドをしなくなったので、書くときにはひたすら文書自体に集中して書いて、ブラウザでHTMLを確認する、というのを続けていけます。

make html をいちいち手でやっていると、どうしても文書ではなくて、ビルドするっていう行為に集中してしまうのです。継続ビルドなら書く方に集中できます。

欠点として、sphinx でビルドするとき、リンクエラーなんかは2回目以降のビルドではでなくなるので、エラーはどんどん流れていってしまいます。大きなコミットする前には make clean; make html  します。

2. ひな形

Komodo Edit の snippet に、ひな形を登録しておき、ワンクリックで挿入します。各 API は、だいたい同じフォーマットで記述するので、便利です。キーボードショートカットも割り当てられますが、そこまで使わないので、クリックで十分でした。

3. 進捗の測定

Komodo Edit のコマンド機能を使って、時刻、文書中に現れる "..todo::" の個数と、ソースファイルの文字数を出力するコマンドを登録しています。作業が一段落したところで、実行して、スプレッドシートにコピペします。スプレッドシートではそれをグラフにしています。

この工程は、作業と関係ないのですが、だらけるのを防止するために、自分の活動が成果物の量としてどのくらい反映されているのかを可視化していました。TODO は書いている途中で増えたりしますし、共通した部分をまとめると、ざくっとソースが小さくなるのですが、それは別に構いません。短期的なやる気の問題です。

echo -e `date "+%Y-%m-%%d %H:%M"`\\t`grep todo:: api/*.rst *.rst | wc -l`\\t`cat \`ls *.rst api/*rst\` | wc -c`

4. Mercurial のログを取り出す

現在の Mercurial ブランチの各リビジョンのメッセージを取り出すというコマンドも作りました。

いきなり完成版のドキュメントを渡すことができなかったので、追記や変更した版を出していくことにしました。そのとき変更履歴を changelog.rst に書くわけですが、コミットの度に編集していると面倒ですし、だいたい内容は mercurial のログと重複します。

そこごで、リリースごとにブランチをきっているので、ひととおり作業が終わったら、このコマンドを実行し、まとめて整形するようにしました。作業後半はこのコマンドがいちばん作業時間の軽減になりました。

hg log -b `hg branch` -v | awk '$1 !~ /(changeset|branch|user|date|files|description|tag):/ {print}'

5. Makefile に upload ターゲット

Makefile に upload ターゲットを追加して、make upload で反映します。これはすぐですね。



以上は、作業の効率化であって、仕事全体の効率化とは別の問題です。こうしたらいいとか、あったらぜひ教えてください。まだまだ書くのが、というか、要件を頭で整理するのが遅いです。困ったものです。

2011-10-14

omake を使わずに、Sphinx ドキュメントの継続的ビルド


Sphinx つかってドキュメント書いているのですが、自動的にビルドできないかなぁと思ったのが、1ヶ月くらい前です。そのときは、EUnitとOMakeでテスト駆動開発とかを読んで omake の P オプションがよさそうだったのですが、実現に至らず。

Mac OS X Lion で Komodo Edit 使って編集しています。omake -P すると、最初に1回ビルドが走ったあと、polling for filesystem changes ってなったまま表示が変わらず、ソースを編集すれどもすれでも、ビルドされません。ターミナルで vim とかで編集/保存すると omake -P はちゃんと変更を検出してくれて、期待通りに継続ビルドになります。

もうファイルシステムとか分かんないので、Makefile を適当に追記して生きていくことにしました。以下追記部分。

auto:
     @while :; do make html_silent; sleep 1; done

html_silent: .built
     @echo > /dev/null

.built: *.rst img/*
     $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
     @touch .built
     @echo
     @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

「make auto」して使います。

auto と html_silent は、.PHONY に入れてあります。

毎秒、.built ファイルと、ソースファイルの日付を比較して、ソースファイルのほうが新しければビルドして、touch .built するだけです。ださくてすみません。

2011-07-31

バグの影響を伝播させないアプローチ - Michael T. Nygard / Release It!

なんとか It! シリーズのひとつ「Release It! 」を読みました。


航空券予約システムをダウンさせたバグの経験から始まります。
極論を言うと、この種のバグがひとつ残らず消滅するなんて夢物語にすぎない。 バグは発生する。消えてくれないなら共存するしかない。 [...] この例で最悪だったのは、ひとつのシステムのバグが全システムに伝播して影響したことだ。答えるべき問いは、「ひとつのシステムのバグが他に影響するのをどうやって防ぐか?」である。 (p.21)

バグの影響を伝播させないシステムの作り方、というのが、この本の「本番用システム」のあるべき姿です。その中からいくつかピックアップ。

本番と同じサイズのデータセットでないと、クエリで数百万行が返っ てきて、それをオブジェクトに変換するときに何か起きるか確かめることかできない。本番と同じサイズのテストデータを使えば、パフォーマンステストでより良い情報が得られるという副次的な効果もある。 (p.93)

ダミーでいいから量をある程度用意するのはよいことだと思う。いまやっているプロジェクトでは、想定ユーザ数と平均的なユーザの典型的な行動パターン(つまり各URLのアクセス頻度)のシナリオを用意してもらえた。まだ調整中なのだけれど、どこがボトルネックになっているのか分かるし、優先順位も明らかになっている。

待たせた結果がエラーメッセージであってはならないし、相手のタイムアウトまで待たせてもいけない。それでは、こちらの問題を呼び出し側に転嫁するだけだ。 (p.116)

フェイルファストっていうのは、なんか努力せずに諦めたみたいな感じがするんだけど、応答が遅い状態が長く続くくらいなら、さっさとフェイルして、アラートメールを出したほうがいい。どうせアラートメール出すハメになるのだから。

開発プロジェクトか大忙しのときに、こうした設計についてのトピックを意識するのは難しいかもしれない。[...] そんなあなたにいいニュースかある。これらの問題を開発中に取り上げるのは断念してもいい。ただし、これは悪いニュースでもある。開発中に収り上げないなら、 本番で取り組むことになるだろう。 (p.235)

開発開始段階ですべての機能/非機能要件が決定されていない(つまりほとんどの)場合、最適な設計も最初に決定できないということになる。少し設計して、作って、また設計して、の繰り返しになるので、設計コストは増加する。そのかわり不適切な設計のままつっぱしって、システムがダウンするリスクを避けられる。

今やっている案件でできていることは続けたいし、できていないことは今後やっていきたいものです。


2011-06-12

節度をもって変化を抱擁する - アラン・デービス「成功する要求仕様 失敗する要求仕様」

今の職場に入ったとき、初めてアサインされた仕事が要件仕様の定義でした。そのときに何冊か買った本のうちの、ひとつが、アラン・デービスの「成功する要求仕様 失敗する要求仕様」です。原題が「Just Enough Requirements Management - Where Software Development Meets Marketing」であるということに、今、気づきました。

ケビン・フォースバーグとハロルド・ムーズによるNASAプロジェクト の研究によれば、NASA(米航空宇宙局)が要求活動を含む計画活動に開発予算の5パーセント以下しか充てなかったプロジェクトでは、総コストは40~170パーセント超過した(図1-11)。一方、NASAが全開発予算の10~20パーセントを計画活動に充てたプロジェクトでは、コスト超過は30パーセント未満だった。 (p.21)

計画活動に時間をかけると、コスト超過が起こりにくい。デービスは他のところでも、この例を挙げて、計画に時間をかけるように勧めています。その最初の段階での成果物が、要件仕様、というわけです。

私は、スケジュールが要求を決めるべきだと強く感じている。 (p.23)

最大の不足するリソースは時間だということを考えれば、必然的にこういう方針になりますね。時間は貯めたり、借りたり、増やしたりといったことができない、とドラッカーが指摘していたと思います。もちろん、時間を節約するために、ヒト、モノ、カネを調達することはできるでしょうけれど、時間自体を増やすことはできません。

どれだけ徹底的に導き出しを行っても、要求は変わっていくものだ。問題は変化していく。そして、問題に対する ステークホルダーの認識も変わっていく。要求を洗練させ、議論するうち、ステークホルダーは新しい要求をさらに思いつくはずだ。それでいいのである。ただ、変化に対する備えはしておこう。決してステークホルダーに「それで、これが最終的な要求なんですね?」などと言ってはならない。  (p.55)

耳が痛いです。最後の要求なのか、とつい聞いてしまいます。

一方で、要求を決めないと、設計や実装ができません。だから、多くのアジャイルなプロセスでは、タイムボックスを区切って、その間は仕様変更をしないというプラクティスがあるわけです。

しかし、約束してもいい。スピードと品質は両立できないものなのだ。  (p.67)

これは心しておかないと。そして、それを痛感するような経験もあります。

  • 要求への変更は、善であり、悪ではない。 
  • 要求変更の流れを阻止しようとしてはならない。 
  • 要求変更の流れを管理できるようにならなくてはいけない。 
  • どの要求を次のリリースに含め、どの要求を含めないかを決定するために、定期的にミーティングを開く。 
  • 10パーセント以上の要求変更を受け入れれば、プロジェクトは失敗するだろう。 
(p.209)

「変化を抱擁せよ。ただし、抱擁できる範囲で。」ということです。その手段として、さまざまなやり方があり、アジャイルな手法はこの前提をもっているように見えます。ですが、デービスはアジャイルがいい悪い、という見方ではなくて、要求のトリアージができるプロセスに、という立場をとっているようです。


2011-05-21

危機が起こったときに備える - エドワード・ヨードン 「デスマーチ第2版」

デスマーチが起こってから、どう対処するか、を、主にマネージャの視点から書かれています。デスマーチが起こることが前提になっているのが、他のマネジメント本と違うところです。プロジェクトマネジメントは計画段階でかくあるべし、と正論を言われても、「いや、もう、プロジェクトはじまっちゃってるし!」ってなりますからね。

しかし、我々が憤れ親しんでき たプロセスに新たにプロセスを追加したり、プロセスを改訂したりする必 要がある新技術を導人するのはずっと難しい。 (p.244)

そうなんだよなぁ。慣れたところから、慣れないところに行くときは、いつもそういうことが起こる。最初から、新しい方法だったらまったく問題は起こらないのに。

納期に遅れたり、予算を大幅に超過したり、プログラマを燃え尽きさせてしまうプロジェクト・マネ ジャーに、プロジェクトを次々と「破滅」させる機会を与えてはどうか?要するに、なぜ、プロジェクト・マネジャーもメンバーも、実際の経験に 備えてデスマーチ・プロジェクトの経験をシミュレートしようとしないの か? (p.252)

デスマーチの練習、っていいなぁ。デスマーチを避けることは大事なんだけど、現実にそれに近い状態が起きるのであれば、それに備えることは意味のあることであるなぁと思いました。爆弾処理班とか、テロ対策特殊部隊とかって、そういう人たちだもんなぁ、などとも。

# 別に火消しになりたいわけでも、管理に意味が無いと主張しているわけでもありません、念のため。

2011-02-13

「Webアプリケーションテスト手法」

「Web アプリケーションテスト手法」なる本を読みました。内容は広く浅く、あんまりテストの経験がない人がテストを始めるとっかかりになりそうです。RoR とか、フレームワークを限定しているので、即物的/具体的なことが書かれています。


Selenium をちょこっと使うことがあったので、読みました。


使いわけとして、たとえば、正しいページに遷移したことを assert で確認し、ページの内容は verifyで確認する、といったことができます。こうすると、ページ遷移が間違っていたら、続けても無駄なのでその場でテスト中断し、ページのの内容は部分的に間違っていても最後まで確認することができます。(p.238)


XP のユニットテストでは100%か0%だけど、どうなんだろ。と思いましたが、acceptance test では、そういうわけではないからいいか。


・要素のロケータは、idが一番テストしやすいので、ページを作るときに適宜id要素を指定しましょう。動作上必要ないところでも、idが入っているとテストが便利になります。
 ・開発とテストを一人てでやっているなら、テストを書きながら必要に応じてidをページのほうに追加していってもいいでしょう。 (p.241)


class での指定は、たしかにやりにくいですね。テストしやすい HTML にする、というのも重要かと思いました。HTML をいじれないときは困るんですけどね。


2011-01-02

トム・デマルコほか /「アドレナリンジャンキー」その1

デマルコの「アドレナリンジャンキー」、読んだあとに付箋はりまくりなので、ちょっとずつ記録に残していきます。自戒ばっかり。
この種の [アドレナリンジャンキーな] 企業文化では、死に物狂いに急ぐことと効率良く成果をあげることが同一視される。このような組織にいたら、中毒にならずにいるのは難しい。切迫感があることがよしとされる。 (p.2)
これ気を付けないとなぁ。短納期なときには、適切にプロジェクトを進めていかないと、これに陥りがち。イベント系の単発システムとか。本来はそれを防止するためのタイムボックス化したイテレーションなのだと思います。

進捗だけでは不十分である。「5月末までに仕様書を50%セント仕上げよう」といった目標では、満足のいく結果は得られない。小さな声がささやく。「50%パーセント、つまり5月末までには終わらないということだ。それまでにほんとうに終えなくてはいけない仕事はどれだったかな。」 (p.7)
つい目先のタスクに「だけ」目がいってしまうんですよねぇ。

プロジェクトに信者がいると、身動きがとれなくなることがある。コンテンツに集中せず、手法戦争を始めるのだ。 (p.33)
これも気をつけなければ。議論がずれていくのは気持ち悪いんだけど、それはコンテンツに集中する、という方向に持っていけてないからなのですね。状況によって修正の方法は違うのだろうけれど、何がコンテンツなのというのを常に覚えておかねば。in-person でやっているときには、ホワイトボードや付箋で、議題を可視化するんだけど、チャットのときが自分にとっての課題です。


2009-12-26

初めてのメンテナンス業務

つい先日、ウェブアプリケーションのメンテナンス業務を引継ぎ、無事完了したのですが、無駄な苦労をしました。課題は3つあって、



  1. 本番環境には SSH でログインできない


  2. 本番環境と、提供された開発環境は同一の構成ではない


  3. システムやツールについて知識が不足している


だったと考えています。最初の2点は私が直接関与できませんが、最後の点について、いろいろと反省点があったので、自戒と未来の自分のために書き残しておきます。



開発環境と本番環境
プロジェクトから提供された開発環境でコードが動作することは、必ずしも本番サーバで動作することが保証されませんでした。それはそれで仕方ないとして、本番環境にできるだけ似せた開発環境を自前で用意すればよかったと思います。それができなかった原因のひとつは、技術的知識/技能の不足です。



MySQL、Apache、PHP それらが chroot された環境で動作するシステムなのですが、これらのコンフィギュレーションの知識がなかったため、自前で用意した環境が信用できませんでした。ほんとになんというか気休めな感じです。



デバッグのツール
基本的に、echo、var_dump、var_export あたりの原始的な関数を使っていました。状況によっては、これらでは足りない場合が出てくるわけです。



で、独自のロギング関数を用意しました。logging_log($obj, __FILE__, __LINE__); みたいな関数で途中記録をためておいて、logging_gethistory(); で記録を読み出すような感じです。これはなかなか便利でしたが、logging_gethistory()に到達する前に、エラーで停止すると見られないというどうしようもない展開です。



プロジェクトが終わってから「PHP デバッグ」で検索すると、いろいろとたくさんツールがあることが発覚。なぜ最初に検索しなかったのかと。



依存関係の把握
Michael Feathers の「Working Effectively With Legacy Code」(レガシーコード改善ガイド)という本を紐解きました。これは変更(デバッグを含む)が困難なコードを、いかに攻略するかについて書かれた本で、その中の依存関係を理解するというところが役立ちました。



たとえば、変数 $foo; の値がおかしい、あるいは関数 $func の動作が怪しい、と。そのときに、どこかで変更を加えていいのかどうか分からないのがレガシーコードです。そこで、この処理や値を変更するとどこに影響がでるか? あるいはどこの値や処理の副作用で、当該箇所の出力が変わるのかを、簡単な図で会ておきましょう、と。非常にシンプルなプラクティスです。



どこかの値がおかしいとき、それらを逆にたどっていくわけですが、依存関係がかなり複雑でエディタでは追えませんでした。そこで、依存関係の図をかくとずいぶんわかりやすくなりました。グローバル変数の副作用があったりして大変でしたけど。








ユニットテスト
頻繁にファイルをアップロードできなかったので、一気にいろんな変更をしてしまい、ハマることもありました。そこで、機能を追加するまえに、手間がかかっても(当たり前ですが)単体テストのコードを書いて、本番サーバで動くことを確認するようにしました。



SimpleTest を使いました。これは、どっかのフォルダに置いておけばいいので、今回のように環境を自由にいじれないような場合にうってつけです。ユニットテストをするほうが、トータルのリードタイムが短くなりました。



今後
と、いうわけで、これらの反省を生かすべく、自宅サーバで簡単なアプリを運用してみようかなぁと思い始めました。そうすればインフラや環境について詳しくはないにしても、何に疑問をもつべきか、くらいは分かるようになるかな、と。ほんとは Google App Engine みたいなのが便利ですが、学習ってことで。



みなさんは、どうやって技能をみにつけているのか気になるので、コメントくださいませ。