今年読んだ本と仕事に役立つおすすめ5冊(2016年版)

意識的に行っている多読の結果報告と、その中でのおすすめを5冊紹介。(紹介している本は今年出版されたものに限っていません。)

ふりかえり

今年の1月末から、意識的に多読をするように心がけています。目標は年間50冊としていて、概ね一週間に一冊です。一応、「エンタテインメントやコミックは除く」という制約を課していますが、哲学系、社会系は含めていて、必ずしもビジネス書だけというわけではありません。結果、今年は55冊ということで、月によって凸凹はありますが、目標としては達成できたことになります。ただし、9月以降は失速しているので、来年は、あらためて計画的に進めないといけなさそうですね。結果的に読んだ本の一覧はこちらです。


読書の管理にはブクログを使っています。web本棚サービスを使って読みたい本を管理するのは初めてでしたが、数値目標を立てたときには、結果を記録するのは必須ですね。非公開の読書メモも書けるので、「あの本にこんなことが書いてあったな」と後から見直すにも便利です。


一覧をざっと見返すと、私自身がSIerから「エンタティンメント系総合商社」という位置づけのユーザー企業に転職したこともあり、分野としてはドメイン知識や組織論が中心で、IT関連も上流寄りになっています。分野の選択をある程度戦略的に行うことで、自分の頭の中にある「目次」が広げられたような気がしています。一方で、ドラッカーの『マネジメント』や、コッターの『リーダーシップ論』、グリーンリーフの『サーバントリーダーシップ』など、「その道の古典」と言える本は積んでるだけでまだ手が出ておらず、来年はこの辺りもきっちりと仕留めたいと思います。古典は一通り読んだうえで、新しく出版された本を読み重ねるような形を早く作りたいものです。


さて、これらの中から、「仕事」という観点で読んで頂きたいおすすめを5冊ピックアップしました。

失敗の本質―日本軍の組織論的研究 (中公文庫)

失敗の本質―日本軍の組織論的研究 (中公文庫)

失敗の本質―日本軍の組織論的研究 (中公文庫)

この本をきちんと読みたくて読書記録をはじめたと言っても過言ではないくらい昔から気になっていた一冊です。太平洋戦争について、敗戦の原因を日本軍の組織論に求めたうえで緻密に分析しているのですが、本書を貫く感情は「怒り」であり、それが本書をいわゆる組織論や軍事評論とはまったく別のものにしています。失敗から学び、それを繰り返さないことこそが祖先に対する敬意であるという強い意志が根底にあり、正直、仕事に「役立つ」と表現することがはばかられます。さらっと読める本ではありませんが、組織運営にすこしでも関わる人ならば読んでほしい一冊です。私も定期的に読み返したいと思います。

サーチ・インサイド・ユアセルフ――仕事と人生を飛躍させるグーグルのマインドフルネス実践法

サーチ・インサイド・ユアセルフ――仕事と人生を飛躍させるグーグルのマインドフルネス実践法

サーチ・インサイド・ユアセルフ――仕事と人生を飛躍させるグーグルのマインドフルネス実践法

  • 作者: チャディー・メン・タン,ダニエル・ゴールマン(序文),一般社団法人マインドフルリーダーシップインスティテュート,柴田裕之
  • 出版社/メーカー: 英治出版
  • 発売日: 2016/05/17
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (6件) を見る
2016年5月出版。アンガーコントロールの本を探していて出会いました。瞑想術の本と言えなくはないのですが、「瞑想」という言葉から通常想起されるような脱現世的な価値観ではなく、ビジネスパーソンあるいは人と関わりながら生きる一人の人間として、心を平静に保ちながら人間関係を構築していくためのプラクティスです。大好きな一文を引用するなら、「相手の気持ちに同意することなく、それを理解し受け入れられるのは、発達した心のあかしだ」(p.243)です。それをできるようにするためのマインドフルネスなんですね。

あなたのチームは、機能してますか?

あなたのチームは、機能してますか?

あなたのチームは、機能してますか?

全体としては、小説仕立ての前半と、フレームワークを説明している後半の二部仕立てです。ポップなタイトルと表紙とは裏腹に、「なぜ組織がうまくいかなくなるのか」が実にロジカルにわかりやすく構造化されています。組織を立て直そう、と思う人にとって、「何を前提としてまずどこに取りかかるのか」は重要課題であり、そういう悩みに対して間違いなく一定の答えを与えてくれる本です。

イシューからはじめよ―知的生産の「シンプルな本質」

イシューからはじめよ――知的生産の「シンプルな本質」

イシューからはじめよ――知的生産の「シンプルな本質」

"知的生産の「シンプルな本質」"という副題がまさに本書の内容を的確に表現しています。適切に「課題」を設定し、それに「解」を与えていくという活動をどのように行うべきかが緻密に書かれています。あるイシュー(課題)に対して、一心不乱に解の質を上げようとするのは労が多くして益の少ない活動であり、まずはイシューの質を高めるべきである、という考え方が基本になります。もうすこし一般化すれば、いわゆる「仮説」思考であり、とにかく目の前の作業に没頭するのではなく、まずは適切な仮説を持って物事に当たるべき、ということを前提とし、それを発展した形であるとも言えるでしょう。いずれにしても、筆者の結果へのコミットメントの強さが、本書を単なるハウツー本ではないものに押し上げています。

27歳からのMBA グロービス流ビジネス基礎力10

27歳からのMBA グロービス流ビジネス基礎力10

27歳からのMBA グロービス流ビジネス基礎力10

「27歳からのMBA」というタイトルが果たして的確だったのかと疑いたくなるほど、ビジネスパーソンであれば絶対に抑えておかなければいけない内容です。「論理的思考力とコミュニケーション力」という基礎力をベースに、企画提案力と実行力という二本柱について語られています。単なるノウハウに留まらず、マインドセットにまで言及しているのが素晴らしいですね。きわめて幅広い内容をコンパクトにまとめているため、一つ一つに関する記述は少なめですが、重要事項の網羅性は高く、その後勉強をするうえでの有益なインデックスとなってくれます。

終わりに

他にもご紹介したい本はあるのですが、「50冊読んで5冊拾い上げる」ということに価値があるように思いますので、これに留めます。上記の中にまだお読みでない本があれば、ぜひ一度お手にとってください。


なお、今年読んで強く印象に残った本を最後にもう二冊挙げておきます。こちらは「仕事」とはまったく別の軸で読んで頂きたい本となります。

殺人犯はそこにいる (新潮文庫)

殺人犯はそこにいる (新潮文庫)

小説ではありません。まだ記憶に新しい「足利事件」の背後で、執念を持って犯人に迫るジャーナリストの方の記録です。

さびしすぎてレズ風俗に行きましたレポ

さびしすぎてレズ風俗に行きましたレポ

2016年6月出版。出版当時は話題になりましたね。タイトルで敬遠されている方もいるかもしれませんが、人と接することにある種の難しさを感じているひとであれば、誰でも心に刺さる内容だと思います(上記、55冊のカウントからは外れています)。

GxPで学んだ、ただ一つのこと

パラダイムを学ぶことと、実際にデリバリーすることとのバランスについて。あるいは転職報告。

導入

9月末を以て、グロースエクスパートナーズ株式会社を退職しました。4年と2ヶ月の間この会社にお世話になっていたことになりますが、その中できわめて多くの貴重な経験をさせていただいたと思っています。退職を決めてから、4年前に自分が書いたエントリ(SIを仕事にするということ)は何度か読み返しました。その中でGxPを表現した次の言葉は、今読んでも正しかったと思います。

ソフトウェアをデリバリーすることには、多くの「泥臭い作業」が伴います。そしてその「泥臭い作業」は本に書かれたり、語られたりすることがあまりない。それはたぶん、語るまでもなく当たり前のことであったり、語れるほどには言語化されていなかったり、語るほど面白くなかったりするせいでしょう。だからといって、「そういうこと」をしなくていいわけではない。このあたり、パラダイムと泥臭い作業をひっくるめた「デリバリー」の全体像が見えずに苦しんでいたわけですが、他にもそういう方はいるのではないかと思います。「バリューストリームの中で自分の手を動かしつつ」「企業をお客様として業務に使うアプリケーションを作る」ことができる場所があるんだということは、多くの方に伝えたいメッセージです。

この4年間のふりかえりとして、この文章にある「泥臭い作業」を言葉にしてみようとしてみたのですが、相変わらず「語るまでもなく当たり前のことであったり、語れるほどには言語化されていなかったり、語るほど面白くなかったり」したので、そのかわり、GxPで学んだマインドセットのようなものについて言葉にしていくことにします。おそらくそれは、自分が求めていた「パラダイムを学ぶことと実際にデリバリーすることとのバランス」にとって、欠かせない要素だったのだと思います。

納得して向き合う

その要素とは何か...と溜めるまでもなく見出しに書いてしまいましたが、「納得感」の一言に尽きます。「そうすることになっているから」「誰がそう言ったから」そういう理由で仕事を進めることは許されませんでしたし、納得できなければ誰に対してでも何かを言える文化だったと思います。では、納得とは何か。分解するなら、一つは「理解」もう一つは「説明」でしょう。どちらも同じコインの表と裏です。プロジェクトに対してなんらかコミットしようと思えば、この納得感を一定のレベルで確保しなければなりませんし、ましてや、プロジェクトのハンドルを握ろうと思えば、それを隅々にまで行き渡らせる必要があります。少し具体的に書いてみます。


「何を」「どのように」「いつまでに」「いくらで」「誰が」作るのか、それら複合した要素をプロジェクトとして組み立てるときにはツールが必要なのですが、この点に関する「これさえやれば大丈夫」という包括的なツールは、僕が知る限りありません。というより、「これさえやれば大丈夫」という感覚がそれ自体すでに危険です。


たとえば....「何を」に関して言うならば、ユースケースなりシナリオなりで対象システムが備えるべき「機能」を何らかのかたちで表現することは誰でもやっていることでしょう。しかし、作るものの性質に応じて、適した記述方法は変わります。画面とER図があればおおよそ大丈夫なこともあれば、なんらかのルールを表形式にまとめなければいけないこともあるでしょう。「設計書とは、あるいは要件定義書とはこのフォーマットで記載するものである」という固定化した発想からは確かに安心感は得られますが、確実に何かを取りこぼします。


さらに、その「何か」を具体的に「どのように」実現すべきかは、ハードウェア構成/ソフトウェアのモジュール構成等いくつかのビュー、いわゆるアーキテクチャで表現されることになりますが、分解された構成要素は「いつまでに」「いくらで」「誰が」というスケジュール/コスト/チームの根拠になります。アーキテクチャの方針によって複雑さは凝集することも点在することもあり、そういった実態を無視して引かれたスケジュールには現実性がありません。


他にもどういうドキュメントを書くのか、そのドキュメント同士はどうつながっているのか、テストとはどう関連するのか、テストの考え方は十分か、など考えなければいけないことを挙げていけばキリがありません。ただ、大切なのは、そういった個別の構成要素およびその関連性について、まずは自分が理解する必要がある、ということです。その理解は、計画時であればプロジェクト計画という作業結果ではなく、その作業を通じて、自分あるいはチームの中で醸成されるものです。そういった「理解」があって初めて、顧客に会社にあるいはチームに「説明」できるようになります。一会社員がプロジェクトに対して果たせる責任といえば、せいぜいがそういった説明責任だけなのです。逆に言えば、誰かのせいにせずに「こういった理由により自分はこれが正しいと考えている」と説明できる立場に立つことこそが、プロジェクトのハンドルを握るということなのだと思います。


余談ですが「自分がこう考えた」ということを基点に置くことは、プロジェクトに限らず、様々な面において後悔しないための一つの方法であるように思います。もちろん、ある種の後悔はするのですが、少なくとも自分のこととして引き受けて、誰かを恨まずに先に進むことはできますからね。そういうことをひっくるめての「納得感」ですね。


***


社員がそういう姿勢で仕事に臨むことについては、どんな企業であっても少なくとも経営陣は望んでいるような気がします。それが様々な事情や経緯で現場レベルで実践できなくなっているケースも多々あるのだと思います。そういうことを求めつつ大きな裁量と共に仕事を任せていただいたこと、また、チームのメンバーが皆、主体的に考え動いてくれたこと、そして、色々なものを放りだして去って行く僕を温かく送り出してくださったこと、そういったことすべてについて、深く感謝します。ありがとうございました。

その後

10月からは、中間流通を軸としたエンタテイメント系総合商社の情報システム本部に勤務しています。立場が変わり、見えるものもだいぶ変わってきました。ユーザー側に移ったことにより、これまでできなかったことでできるようになったこともあると思います。ただ、根底にある「ものづくり」という本質はそう変わるものでもなく、これまでの経験を活かして引き続きがんばります。また、言葉を紡ぐ作業はこれからも続けていきたいと思いますので、どうかよろしくお願い致します。

ソフトウェアアーキテクチャの先に

最後に、少し宣伝めいたことを。10/1付で新しい翻訳書が出版されました。ちょうど転職のタイミングとなってしまいましたが、僕がGxP名義で出版する最後の翻訳書ということになります。編集の上野様、パートナーの岡澤さんにはいつものように多大なるご迷惑をおかけしました。また、10年前に書かれた本を訳させてほしいという私の勝手なお願いを引き受けてくださった翔泳社様に深くお礼申し上げます。


内容については、ゆうすけさん、今野さんのインタビューがこちらに掲載されていますので、興味のある方はどうかご覧ください。「結局のところ、アーキテクトは考えるという行為から逃れられない」という、ある種このエントリで書いてきた内容とつながるところがあるのですが、そういう、考えながら前に進もうとする人に対して、多くの気づきを与えてくれる名著だと思います。

ビヨンド ソフトウェア アーキテクチャ (Object Oriented Selection Classics)

ビヨンド ソフトウェア アーキテクチャ (Object Oriented Selection Classics)

パラダイムを学ぶことと、現実の中で活かすこと

本に書かれた内容を実際に使うことの難しさについて

導入

ここ一年ぐらい将棋にハマっているのですが、最近は棋力が上がらなくて悩んでいます。序盤で失敗して一方的に敗けるのが嫌なので、序盤から中盤の入り口について解説してある戦型の本をよく読んでいるのですが、いまいち「強くなった」という気がしません。原因は明らかで、「知らない形に出会った時に安定して力が発揮できない」のです。初めて見る局面でも自信を持って指せるようになりたい。多分私が触れたいのは、定跡とか手筋を超えたところにある「棋理」みたいなものなんでしょう。そんな話をあるプロ棋士の先生にしたときに言われたのが「お勉強しても強くなれませんよ」という言葉で、それが妙に耳に残っています。


似たようなことはシステム開発にも言えるのではないか、ということを最近よく思います。そのことについて、「ドメイン駆動設計(DDD)」や「スクラム」を例に言葉にしていきます。


DDDにせよ、スクラムにせよ、パラダイムとして優秀であることに疑う余地はありません。ただ一方で、「DDD/スクラム開発をしようとしているが、いまいちうまく行っていない」という話も時折耳にします。こういう「うまく行かなさ」は何に起因するのでしょうか。

書き留められたパラダイムと現実のコンテキスト

特定のパラダイムを教える立場にある人なら、そのパラダイムを正確に理解することは欠かせません。ただ、プロジェクトやビジネスを推進している側からすれば、「優れた手法を取り入れることがプロジェクト(ひいてはビジネス)の成功につながるとは限らない」といえます。理由の一つとして、プロジェクトやビジネスが、置かれている環境や構成要因、対象とするドメインの性質、文化といったさまざまなコンテキストに束縛されていることを挙げられます。あるパラダイムが一つの体系として書き留められる場合、そうしたコンテキストは語られないため、そもそも適用段階で具体的にどのようにしたらよいかわからないことになります。適用の方法についての議論は、こうした「どのように」が多いような印象を受けています。ただ、こうしたことについてはそれほど深刻とは思えません。適用事例が増え、ノウハウが蓄積されるにしたがって自然と解消していくものでしょう。


より深刻なのが、「何をするべきか」という問題です。「DDD/スクラム実践する」と、パラダイムが目的化してしまった場合、「何をすればDDD/スクラムをしていることになるのか」というメタな議論が生じます。こうした関心の横滑りはきわめて危険で、DDD/スクラムを正しく実践することに意識が向くと、プロジェクトの本来の目的から逸れてしまいます。DDDで言えば、モデルの正しさを追求するあまり、複雑化しすぎて誰にも理解できなくなったとしたらどうでしょう。あるいは、スクラムの実践に取り組み、「タイムボックスによる定期リリース」ができていたとして、バックログに積んであるものがバグフィックスだけだったとしたら、それはうれしいことなのでしょうか。それがうれしいとしたら、誰にとってなのでしょうか。

実践から得られるもの

わかりやすくするために極端な例を挙げましたが、現実に特定のパラダイムを理解し、そのパラダイムの価値を自分なりに解釈し、実際の文脈の中で必要なことを行って成果に結びつけるのは、決して簡単なことではありません。私自身は大筋、DDDであれば、適切なドメインの分析とドメインの特性に応じたモデリングアーキテクチャ設計が、スクラムであれば、ゲームのルールに従うよりも組織全体のバリューストリーム/フィードバックループの構築が骨子だと考えていますが、具体的なコンテキストに置かれれば、また違った解釈が必要になることもあるでしょう。後から振り返れば教科書通りで最適だと言える判断も、その場に置かれたときに行えるというのは、まさに「お勉強」ではたどり着けない場所で、実際どうすればそういうことができるようになるのか、正直私もよくわかっていません。差し当たりは、視野を広げ、「全体」という言葉で自分に見えるものを広げつつ、一歩ずつ考えながら進んで、「何かがおかしい」という違和感や、あるべき姿に対する感覚を磨いていくしかないのではないかと思っています。

GxPでは人材を募集しています

今の会社に入って一番よかったと思うのは、会社の規模的に正しく手を伸ばせばどこまでも届く反面、プライムという立場でお客様と直接やりとりができることです。すべてがうまくいくわけではなく、苦労がないわけではありませんが、「全体」を見渡しつつシステムを通じて世の中と関わるという実感は、間違いなく得られる場所だと思っています。入社してからの取り組みについては、以前ご報告させていただきましたので、よろしければ、こちらもご覧ください(動画はこちらで公開されています)。なお、弊社はいつでも人材を募集しています


弊社に興味を持ってくださった方で、いきなりサイト登録するのがためらわれるという場合には、お気軽にお問い合わせください。メール(digitalsoul0124 at gmail.com)やfacebookなどでのご連絡もお待ちしております。

「アジャイル」と「ウォーターフォール」

アジャイルがダメだと思う7つの理由へのだいぶ遅い反応

導入

もうだいぶ前の話になってしまいましたが、アジャイルに関するブログエントリ「アジャイルがダメだと思う7つの理由」は予想以上に波紋を呼び、それに呼応していくつものエントリが公開されました。発端となったエントリに関していうと、通常であれば必要となる数々の留保事項や前提事項を書かずに切り込んでいるという点において、間違いなく「煽り」であると言えます。ただ、「アジャイル」という言葉をとりまく数々の事象をかなり的確に指摘することによって、「アジャイル」という言葉をパブリックな場所で語っている少なからぬ人たちに対して、(意識的か無意識的かは問わず)ある種のポジショニングを強いたという意味で、実にいい釣り針なのではないでしょうか。


個別の論点に関する「アジャイルでは全体スケジュールにコミットできない」「いや、アジャイルだって全体スケジュールにコミットできる」という議論は一通り終わった感がありますので、だいぶ出遅れた立場としては、少し違う視点から整理したいと思います。

ところで、「アジャイル」と「ウォーターフォール」は何が違うんでしょうね

反射的に出されるであろう主張とそれに対する反論をいくつか挙げてみます。「ウォーターフォールは出来上がるまでの時間が長い」―本当ですか?ウォーターフォールは一年かけなければならないなどという定義はどこにもありません。「ウォーターフォールではフィードバックを受けられない」―どうでしょう?重要な機能なら必要に応じてプロトタイピングをするなどということは常識です。「ウォーターフォールでは変化に適応できない」―そうでしょうか?顧客がソフトウェアを触るようになって上がってきた諸々の仕様変更を、テストの進捗を考慮しつつ、別の線を引きながら取り込んでリリースに間に合わせる、みたいなことは普通だと思います。また、アーキテクチャ策定段階で、比較的安定している機能と後々変化が予想される機能とで設計/実装方針を区別し、変化に対応しやすい(その代償としてコストのかかる)設計にしておくといったことも当然やるべきことです。


また、ウォーターフォールの基本的な性格とされる「要件定義」→「設計」→「実装」→「テスト」という工程は、フィードバックサイクルの長短に関わらず必要なことであり、これを以てアジャイルウォーターフォールの違いを語ることは難しいでしょう。「基本設計書がすべて出そろってレビューが完了するまでは実装に着手してはならない」といった教条も時折は耳にしますが、それがウォーターフォールのあるべき姿かどうかという疑問と共に、現実としてそこまで厳格な運用が行われているケースはごく稀ではないかと思います。実際には、サブシステム(あるいは大機能など、呼び方は色々あると思いますが)といった、一定の粒度でシステムを分割し、各工程を並行させると共に、実装も着手できるところから着手しているでしょう(そのリスクを発注側と受注側のどちらが持つかは別として)。


つまり、現実の開発においては、「変化の方向性を先読みしつつ、フィードバックを適切に受け取ること」が重要なのは間違いなく、そのための施策は色々とあるわけです。ただ、そういった諸々の施策を「アジャイル」と呼び、そうではないものを「ウォーターフォール」と呼んで思考停止するのは、色々と不幸なことを引き起こすと思うのです。一方で、アジャイルの主張する「変化への適応」に気を取られすぎて、「事前に考えること」を否定することも逆方向の思考停止です。「それは後で考えれば良い」という台詞は、後で発生するであろう揺らぎを吸収できるスケジュールとアーキテクチャに支えられてはじめて説得力を持ちます。事前に計画を立てること、全体像を描くこと、全体の中で個別に戦略を考えること...総じて先のことを予め考え、読んでおくことは常に必要です。そういった「考えること」の放棄に「アジャイル」という名前を冠することに対しては、同様に強い危機感を覚えます。


結局のところ、「考えてわかることは、考える。考えてもわからないことは、どうすればわかるかを考えたうえで、そこまで手を進めてみる」という基本がすべてで、それ以上でもそれ以下でもないということです。


とはいうものの、世の中に目を向けてみると、ここまで「アジャイル」という言葉が盛り上がる背景には、"これまでの"開発がうまくいっていないという感覚あるいはある種の閉塞感が存在するのだろうとは思います。つまり、批判されるべき「何か」は現実に存在し、それへの批判として別の「何か」が存在するのは事実で、それぞれがウォーターフォールアジャイルという名前をまとっているだけではないか、と感じるのです。それが一体何であるのか、普段受託開発を行っている自分の経験をふりかえりながら言葉にしていきたいと思います。

チームと文化

批判されるべき「何か」は、プロセスや成果物ではなかろう、ということが出発点です。プロセスにしても成果物にしても、コンテキストに応じて適切なものを選んでいけばよいものだからです。むしろ「コンテキストに応じて適切なものを選ぶ」ことができない環境にこそ問題視すべきだと考えます。

単能工とコマンドコントロール

基本設計書を書くだけの"SE"。設計書の内容を実装するだけの"PG"。よく話題にあがりますね。「それではダメだ」という批判と、「実際にそういう人たちが集まってシステム開発をしているんだ」という現実路線の両方が存在すると思います。僕自身の経験に照らせば、一定以上の規模であれば、実装がある程度わかる人が設計書を書き、設計の矛盾に気づける人が実装し、双方のコミュニケーションが円滑に進んでいる状態が現実的で健全な開発であるという気がします。それでも工程ごとに担当者が分かれれば、そのコミュニケーションを繋ぐための中間成果物が必要になるのであり、そういう中間成果物が「お客さんと要件を詰め、過不足のないドキュメントを描き、柔軟かつ統一のとれたアーキテクチャでそれを実装して、鉄壁のテストをやってくれるエンジニア」から見て無駄なオーバーヘッドに見えてしまうのは仕方が無いことだと思います。そういうエンジニア2,3人で開発できる規模なら、また違ったやり方もできるでしょう。


工程ごとに担当者が分かれていても、工程間がスキル的にもコミュニケーションパス的にもゆるやかにつながってフィードバックが回っていれば、それほど問題にはならないと僕は考えています。問題なのは「あらゆる指示が上から降ってきて、ただ決められた期日までにその指示をこなす」ことが求められる環境です。これを生み出すのは、同一工程内の階層化、具体的に言えば、少数の「タスクを分解し指示する人」と多数の「指示に従ってタスクを遂行する人」との分断です。工程ごとに作業者が分かれていて、かつその作業が上から降ってくるという構造であれば、メンタリティとして成果物の提出先が「仕事を割り当てる人」になってしまい、それを受け取る次工程の人が忘れ去られてしまうのもわからなくはないですが、当然いい結果は生まないでしょう。


個人的には、この「分解されたタスクの指示書」がWBSの姿をしていることこそが、この構造が「ウォーターフォール」と呼ばれてしまう原因ではないかと思っています。ただ、もはや開発モデルとしてのウォーターフォールは関係ないですよね。ここにあるのは、「多重階層によるコマンドコントロール」と「成果物を壁の向こうに投げつけるサイロ構造」です。フィードバックが届かない多重階層構造によって人が病み、サイロ構造によって成果物の品質も下がるのはよくわかります。

多能工と自己組織化

「お客さんと要件を詰め、過不足のないドキュメントを描き、柔軟かつ統一のとれたアーキテクチャでそれを実装して、鉄壁のテストをやってくれるエンジニア」に話を戻します。そんなスーパーマンがいたとして、その人も始めからそうだったわけではないでしょう。要件定義、設計、アーキテクチャ策定、実装、テストといった各工程を個別に経験する中で知識と技術を蓄積した結果にすぎません。つまり、多能工になるためには、工程を横断して様々な経験をする必要があるわけですが、前述の文化ではその経験を積むことができません。


「多重階層によるコマンドコントロール」では多能工が育たず、多能工が育たないから「多重階層によるコマンドコントロール」が必要になるのです。この負の連鎖を断ち切るために、単能工とコマンドコントロール、多重階層の関係をもう少し整理しておきます。作業をする人が見ることのできる範囲が狭い場合、その狭い範囲に応じたチャンクにまで仕事を細分する必要があります。そして、その負荷は、指示をする人に集中することになります。一人で見ることのできるノードの数には限りがありますので、作業者が食べられるサイズになるまでに階層ができあがるのはよくわかります。


つまり、「指示を出す人/従う人」という構造に無理があるのです。では、違う道はどこにあるのでしょうか。「正解」ということではないのですが、受託開発のリーダーという立場で自分のやっていることは、「指示出し」ではありませんので、少しふりかえってみたいと思います。


...さて、僕は何をやってるんでしょうね。感覚を言葉にすると、対チームとしては「大きな方向づけと情報の流れのコントロール」、対顧客としては「窓口」ですかね。考えてみると、作業の細分化はほとんどメンバーに任せてます。得意分野を中心に緩やかに周辺をカバーできるメンバーに囲まれているので、渡せる仕事のチャンクが大きく、作業分割と統合にコストがかからないんですね。コストがかからない、というよりそこに自分が入ると、自分がボトルネックになって効率が落ちてしまう、という表現が正しいです。また、大きな方向づけと書きましたが、進め方も自分だけで決めているわけではなく、あまり時間を気にせずに、よく議論している気がします。とはいえ、和智「どうしたらいいと思います?」○○「こうですかね?」和智「じゃあそれで」○○「」というパターンが頻出している気もして、エントリを書いていて、改めてメンバーに恵まれていると思いました。要は自分がボトルネックにならなければ順調に開発が進むわけで、ボトルネックにならないコツは、情報の流れをプッシュ型からプル型に変換することだと思っています。「大きく渡して、必要に応じて聞いてもらう」というやり方ですね。

終わりに

ソフトウェア開発を生業としていれば、さまざまな業務に関わることになります。やることが大きく、作るべきものが最初から分かっている業務であれば、コストを抑える代償に変化を想定しないアーキテクチャで進めることができるでしょうし、そもそも何をするかもわからないうちから実験的にモノづくりを始める状況であれば、変化に柔軟に対応できるアーキテクチャをインクリメンタルに膨らませていくべきでしょう。


重要なのは常に、「何のために何を作るのか」という目的であって「どう作るか」は手段でしかあり得ません。それに対してエンジニアとしてできるのは、専門性を高めることと守備範囲を広げることを通じて、どういう要求にも対応できるようになっていくことかありません。顧客が満足するものを提供しつつ、プロジェクトを通じてメンバーが成長できるのであれば、開発手法は別に何でもいいと思うのです。

テスト駆動開発の進化

デブサミ関西2012での講演内容まとめ

はじめに

今月、GOOS日本語版が発売されました。

実践テスト駆動開発 (Object Oriented SELECTION)

実践テスト駆動開発 (Object Oriented SELECTION)

継続的デリバリーに続き、高木さんと一緒にお仕事をするのはこれで二冊目です。今回も多くの人に助けられて、目標としていたデブサミ関西での出版にこぎつけることができました。関係者の皆さま、どうもありがとうございました。


講演では触れませんでしたが、ここで「実践テスト駆動開発」というタイトルの由来について少し書いておきます。原書のタイトルはご存じの通り、"Growing Object-Oriented Software, Guided by Tests"で、前半の頭文字をとってGOOSと呼ばれています。"grow"も"guide"も本書にとって外すことのできないキーワードでしたので*1、タイトルから外すことは考えられませんでした。ただ、これをうまく収めるタイトルがどうしても浮かばなかったため、直訳を副題としました。主題に今の名前を付けたのは、ケント・ベックの著作(Test Driven Development: By Example (Addison-Wesley Signature Series (Beck)))以来、TDDにとっては二冊目のバイブルという位置づけを大切にしようと考えたのです。


講演のタイトルを「テスト駆動開発の進化」としたのは、2002年のケント・ベックの著作と比べてGOOS本はどういう所が変わっているのか、という点にフォーカスしているためです。


さて、今回の講演のアジェンダです。

  • テスト駆動開発の進化とは?
  • GOOSのポイント
    • 進化という観点だけではとらえきれないGOOSのポイントについてご紹介します。
  • 開発の現場で活かすには?
    • GOOSで紹介されるテスト駆動開発を実際のソフトウェア開発のライフサイクルに組み込むにはどうすればいいかを考えます。


それでは内容に入っていきましょう*2。当日使ったスライドはこちらです。

テスト駆動開発の進化

そもそもテスト駆動開発とは、プロダクションコードに先立ってテストコードを書く(テストファースト)ことで、コードを動かしながら完成に近づけていくという手法です。まず失敗するテストを書き(レッド)、そのテストを通るようにし(グリーン)、先に進む前にリファクタリングする、というリズムは、黄金の回転と呼ばれたりもします(詳しくは(こちら)。


具体的にFizzBuzz*3を使って例を示します。TDDでは、プロダクションコードを書く前に、まずはテストコードを書きます*4

   @Test
    public void when1then1() throws Exception {
        FizzBuzz ex = new FizzBuzz();
        String result = ex.result(1);
        assertEquals("1", result);
    }

シンプルなテストですが、これだけでも多くの判断をしています。

  • クラス名はFizzBuzzにしよう
  • メソッド名はresultにしよう(これでいいの?)
  • 戻り値は文字列にしよう("fizz"と"buzz"があるので)

この状態では実コードがありませんのでコンパイルも通りません。コンパイルを通しながら、最もシンプルな仮実装をするとこうなります。

public class FizzBuzz {
    public String result(int i) {
        return "1";
    }   
}

かならずしもreturn "1";である必要ななく、return Integer.toString(i);でも構いません。どこまで先に進んでいいかということについてTDDでは特に何も定めていないのです。重要なのは、「何をテストすべきか」という自分の理解を深めながら先に進むことです。個人的には、まずはできる限りシンプルなテストと実装(この場合であれば「1を渡したら"1"が戻る」)でインターフェイスを定め、その後で仕様に関するテストを計画的に書いていくのが良いのではないかと思っています。


さて、テストがグリーンになったところでリファクタリングです。resultというメソッド名がなんとなく気になりますが、今は先に進みましょうか。またテストを書いてレッドにします。インターフェイスが定まったので、次は仕様のテストですね。今回は、数字のテストを置いておいて先に"fizz"をやりました。

    @Test
    public void 引数が3だったらFizz() throws Exception {
        FizzBuzz fizzBuzz = new FizzBuzz();
        assertEquals("fizz",  fizzBuzz.result(3));
    }

これを通すためにコードはこうなります。

    public String result(int i) {
        if(i % 3 == 0) {
            return "fizz";
        }
        return "1";
    }

これでふたつめもグリーンです。中身もとりあえず良さそうですね。こうして、5の倍数だったら、公倍数だったら、数字だったら、とテストを書いていくと、動かしながら完成品が出来上がります。テストがあるのでリファクタリングを安心してできるところも、TDDのいいところですね。


ここまで読んで頂いておわかりの通り、TDDは何かひとつのクラスを実装するには非常に優れた手法です。しかし、普段我々が携わっているようなアプリケーション(私の場合はWebアプリケーションになります)では、どこから手を付けて良いのかわからなくなってしまいます。こうした疑問に対して、スティーブとナットはやはりテスト駆動開発の黄金律を使って立ち向かいます。その黄金律とはすなわち、まずは失敗するテストを書くというものです。ただし、そこで彼らが最初に書くテストは、「受け入れテスト」です。受け入れテストでテストするのは、システムが実装すべきフィーチャ、顧客に対して提供すべき価値ということになります。そしてこの受け入れテストはエンドツーエンドに実施しなければいけません。エンドツーエンドとは、システムの「端から端まで」、たとえば、ユーザーインターフェイスからデータベースまでということです。


この受け入れテストから書き始めるTDDは次に示すような二重のループを描くことになります。


外側のテストは実装すべき機能がどこまでできているかを示す進捗の指標となり、内側のテストによってコードの質が高く保たれます。この外側のループと内側のループを繰り返し辿ることで、ソフトウェアが作られていきます。本講演のタイトルにしたテスト駆動開発の進化とはすなわち、コードを書くことから、ソフトウェアを作ることへということなのです。

GOOSのポイント

さて、前述した二重のループに加え、GOOSにはいくつか特筆すべきポイントがあります。ここではそれを二つとりあげます。それが、モックウォーキングスケルトです。

モックについて

ケント・ベックJUnitの作者であるように、スティーブとナットはjMockの作者です。jMockは二人のオブジェクト指向観をベースに作られています。彼らはオブジェクト指向システムを、「協力しあうオブジェクトの網の目」ととらえます。クラスの継承関係よりも、オブジェクトがどのようにコミュニケーションし合うかが大切だと考える彼らにとっては、クラスよりもインターフェイスが重要です。


さて、そのような発想に従い、網の目としてオブジェクトが構成されていると、テストをするのが難しくなってしまいます。あるオブジェクトをテストしようと思うと、隣り合う他のオブジェクトも動いてしまうことになるからです。


そこで登場するのがモックです。モックを使うと、隣り合う他のオブジェクトをニセモノと差し替え、テストの際に思い通りの動きをさせることができます。この話は具体的な方がわかりやすいので、具体例として「モックによるインターフェイスの発見 - Digital Romanticism」をご紹介しました。興味のある方はご一読くださいませ。なお、前のブログの方には書いていませんので、テストコードの表現がシーケンス図と同じになるという点をここで強調しておきたいと思います*5


キーに対して値をロードして返すシーケンス図がこちら:


実装がこちらです

@Test
public void キャッシュされていないオブジェクトはロードする() throws Exception {
 
    final ObjectLoader mockLoader = context.mock(ObjectLoader.class);
 
    context.checking(new Expectations() {
        {
            oneOf(mockLoader).load("KEY");
            will(returnValue("VALUE"));
        }
    });
 
    TimedCache cache = new TimedCache(mockLoader);
    assertThat((String) cache.lookup("KEY"), is("VALUE"));
}


例に示した仕様ではオブジェクトローダーが事前に存在することが想定されていましたが、実際にテストを設計しながらインターフェイスが発見されるということも十分に起こります。受け入れテストからはじめて、外側から内側へ、インターフェイスを発見しながら開発を進めていく点がモックを使ったTDDの特徴になります。モックを使ったテストコードを書くことは設計の活動ですので、テストファーストとはいえコードを書くことにこだわりすぎず、色々な図を使いながら考えて進めていくのが良いのではないかと思います。


そのようにインターフェイス(あるいはオブジェクト)を発見するためのパターンがGOOSには三つ出てきます。

分解(Breaking out)
あるオブジェクトが責務を持ちすぎている場合、ふるまいの凝集した単位を分割するというものです。
発芽(Budding off)
新しい概念が登場したときにプレースホルダー型をラップする(値)もしくは、サービスを定義する(オブジェクト)というものです。分解は既にあるものを分けるのに対して、発芽は新しい概念が導入される点が特徴です。
包含(Bundling up)
関連するオブジェクトの集団をひとつのオブジェクトにまとめるというものです*6。上記二つは分けるパターンだったのに対して、これはまとめるパターンです。

これらのうち、分解と包含は基本的な考え方だと思いますが、発芽はきれいなコードが書けるようになるためのいいテクニックだと思います。


なお、もう一つ重要な点として、ビジネスロジックの中核は、ビジネスドメインの言語を使うべきだ、というものがあります。これについて詳しく知りたい方は、ぜひ以下の本をご覧ください。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

ウォーキングスケルトンについて

GOOS流のTDDでは、フィーチャの開発とエンドツーエンドテスト基盤の開発を並行して進めなければなりません。しかしこれには、テストが失敗したときの原因がどちらにあるかわからないという点を始めとした難しさがあります。ウォーキングスケルトン(Walking Skeleton)とは、これを解決するための方法です*7。まずは、できる限り薄いフィーチャのスライスを取り出し、それをビルド/デプロイ/テストするための基盤を作ります。こうした基盤が一度できれば、あとは少しずつテストを増やし、プロダクションコードを育てていくことができるようになります。詳しくは以下の本をご覧ください。

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

中間のまとめ

これまでの内容をいったん整理します。まず第一に、GOOS流TDDの核心は受け入れテストとユニットテストにより構成される二重のループにあります。それを円滑にまわすために、まずは薄いスライス(ウォーキングスケルトン)を使ってテスト基盤を作り、フィーチャを増やしていくという形をとることになります。第二に、テスト駆動開発という名前がついてはいますが、ここで言われているテストはシステムのふるまいです。つまり、テストを考えるということは、システムを外側から見たときのふるまいを考えるということに他なりません。こうした考え方については、「BDDの導入 - Dan North - Digital Romanticism」をご参照ください。

開発の現場に活かすために

さて、ここまでGOOSに書かれたTDDの特徴を見てきました。自社システムの開発を数人で行っているチームであれば、ここまでのノウハウで十分にうまくいくと思います(もちろん、スクラムのようなある程度の開発プロセスに関する方法論は必要でしょうが)。しかし、プログラマ30名〜40名で行う受託開発を考えると、なかなかこの形に持ち込むのが難しそうです。最後に、こういった中規模〜大規模の開発現場でGOOSを活かすにはどうすればいいかを考えていきたいと思います。


二重のループをどう開始すればよいか、という問いは、二重のループの外側を見ることでしか解決できません。二重のループに入る前の段階として、GOOSには以下のステップが説明されています。

  1. 問題を理解する
  2. アーキテクチャを定める
  3. ビルド/デプロイ/テストを自動化する


ここで重要なのは1.と2.、特に2.のアーキテクチャです。GOOSが想定しているアーキテクチャは、タイトルにある通り、「オブジェクト指向ソフトウェア」です。しかし、ここで想定しているような中規模〜大規模の開発では、すべてを「オブジェクト指向」で作ることはあまり現実的ではありません。まず重要なのは、業務分析を行った上でドメイン(ここでは「機能の大分類」と読み替えて頂いても構いません)を抽出し、それぞれのドメインごとに特性を分析することです。


ここで分析すべき特性とはすなわち、業務の複雑さをどこで受け止めるべきか、ということです。ここでは二つ分類を出してみます。

  1. エンティティ主体のドメイン
  2. ロール主体のドメイン

エンティティ主体のドメインは、普段多くの方が目にしているものだと思います。つまり、業務の複雑さがデータモデルに吸収され、E-R図とSQLビジネスロジックの中核になるようなドメインです。このようなドメインであれば、ムリにオブジェクト指向的に作る必要はなく、SQLを気持ちよく書くためのSQLテンプレートエンジンを準備した上で、入力チェック、DBアクセス、編集処理といった手続きをトランザクションスクリプトとして実装することが正解となります。このようなドメインに対してO-Rマッパーを当てはめた結果、コード内に大量のJPQLが埋め込まれ結果として可読性が落ちてしまうケースを見たことがある方も多いのではないでしょうか。また、こうしたドメインの場合、テストとして設計するべきは、シナリオではなく、データのパターンになります。ユーザの操作だけで見ると「詳細画面を表示する」という一シナリオで済んでしまうところ、「このフラグがこうなっている場合はこの項目は表示しない」、あるいは「ここがこういう状態の場合にはリレーションのこっちから値を持ってくる」といったパターンが出てくることになります。


一方で、処理が複雑で、ロール間のインタラクションとしてモデル化した方が適切だと思われるドメインも中には存在します。例としては、決済処理や権限といったよくある共通処理、あるいは外貨金利の日またぎ計算といった特殊な業務ロジックが挙げられます。このようなドメインに対してトランザクションスクリプトで立ち向かってしまうと、処理が重複したり、メソッドが長くなりすぎたり、という問題が発生します。またテストの設計も、「こういう条件でこういう入力があったらこのインターフェイスを呼び出す」という形のシナリオになるでしょう。


ここでは二つの分類を出しましたがこれが二つである必然性はありません。各ドメインを記述するのに適切なパラダイムは他にもあり得ると思います。重要なのは、作るものの特性を見極め、それに合わせたアーキテクチャを策定しようということです。GOOSではソースコードは「育てる」ものだとされていますが、そのソースコードが育つ方向性を事前に規定するのがアーキテクチャなのです。

*1:詳しくは本書の序文を読んでみてください

*2:内容については、一部後から追記しています

*3:wikipediaFizzBuzzを参照

*4:このコードは行きの新幹線の中で @yusuke_arclamp とペアプロをしました。ご協力ありがとうございました。

*5:これは行きの新幹線の中で、上司の@yusuke_arclampから指摘されて気付きました。静的な構造を表現するクラス図より、動的な動きを表現するシーケンス図の方が、モックとの相性が良いのです

*6:日本語版p.64で包*括*となっていますが、ミスによる表記揺れです。申し訳ありません。

*7:直訳すれば歩くスケルトンで、骨格だけで筋肉のないガイコツが動いている、というイメージです。継続的デリバリーおよびGOOSでは、意味を取って動くスケルトン、としています

静と動の往還としてのモデリング

DCIを参照しつつ「業務分析」について考える

はじめに

最近「アジャイル」という言葉をなるべく使わないようにしています。なぜなら、この言葉に込められた「桃源郷へのあこがれ」が、色々なものを見えなくしてしまうような気がするから*1。例を挙げましょう。アジャイルの基本は、「タイムボクシングによるインクリメンタルかつイテレーティブな開発」であると言え、それを実現するために流派によって様々なテクニックが提示されます。Scrumを見てみましょう。Scrumを実現するために絶対的に必要なのは、プロダクトオーナーによって適切に優先順位付けされたプロダクトバックログなわけですが、網羅性と整合性を保証したかたちでバックログアイテムを優先度順に並べるって、実はものすごく難しいことを言っていませんか? 確かにタイムボクシングによる軌道修正がある程度できるとは言え、「優先順位の低いところは粒度が粗くてもいいよ」で先々問題が起きないとは、私にはどうしても思えないのです。どんなにプロダクトオーナーが先を読んでいても、どれほどチームが優秀でも、プロダクトオーナーとチームとの間で最終的なアーキテクチャが共有されていない限り、コードが先々の変化に耐えられるかたちになっているとはどうしても思えない。


逆に言えば、網羅性と整合性が保証されたかたちでプロダクトバックログなり機能一覧なりが整備されていて、それを実現するためのアーキテクチャが定まっているのであれば、「それは、もう真っ当にウォーターフォールをやってもうまくいくのでは?」という気もします。しかも、継続的デリバリーのようなテクニックをウォーターフォールでは使えないかというと、それはまた別の話。


こうして見ると、アジャイルウォーターフォールかという区別は「アップフロントでの作業云々」ではなく、「何を固定するか」という話と社内事情とを照らし合わせたときに出てくる解ととらえるとすっきりします(これもよく言われることではありますが)。つまり、自社メンバだけで開発するという前提ならリソース固定(アジャイル)にならざるを得ないし、「どうしてもこの日までにこの機能を」というのがお客様のビジネスにとって必要なら、リソースのスケールアウト戦略(ウォーターフォール)を考える必要が出てくるでしょうし。


前置きが長くなりましたが、こうしたことを踏まえて、いずれにせよ必要になるであろう「アップフロントな分析・設計作業」が本エントリの主題です。

ソフトウェアをデザインする

業務を分析してソフトウェアを設計する作業は、抽象度をコントロールしながらシステムのモデルを作り上げていく作業です。どういうものを書かなければいけないかは、教科書を見ればいくらでも出てきます。業務フロー、機能一覧、ER図、画面遷移図、入力チェック仕様、データアクセス仕様、クラス図、シーケンス図 etcetc・・・。このとき重要なのは、こうした複数のモデルが、1つのシステムの別々の側面を記述しているものであること。つまり、モデル1つ1つが、明確につながっていなければならないということです。このつながりが見えているかどうかが、設計の整合性と網羅性に決定的な影響を与えます。


このつながりを考える上で、大きなヒントを与えてくれるのがDCIアーキテクチャです。

システムが「どのようなものか」/「何をするのか」

DCIの根底にある考え方を簡単におさらいします。DCIでは、人間がシステムをとらえる際には「システムがどのようなものであるか」と「システムが何をするか」の2つから考えているとします。その上で、オブジェクト指向のことを、後者を表現することには長けているが、前者をうまく表現することはできないと批判します。さらに、「人間のメンタルモデルを表現する」というオブジェクト指向の理念に従い、この「システムが何をするか」と「システムがどのようなものであるか」という異なる位相で重なり合う2つの軸を表現するために、ロールという概念が導入され、実装のためにScalaのTraitが提示されます(詳しくはこちら)。


これを実装の話としてだけとらえてしまうと、実務で使うにはちょっと怖い感覚があるのですが、システムを「どのようなものか」と「何をするか」という2つの軸でとらえるという発想は、システムをデザインする上で大きなヒントを与えてくれます。この軸は「構造とふるまい」であり、「静と動」であり、「空間と時間」でもあるのです。

静的なモデルと動的なモデル

業務分析の入り口は、ユーザーの動きです。システム以前に、ユーザーが何をするのかがまず問題になるということですね。これを表現するためのテンプレートとしては、業務フローやユースケース、あるいはユーザーストーリーなど、そのコンテキストに応じて適切なものを選択すればよいですが、表現したいのはユーザーがシステムで何をしたいのか、それを踏まえてシステムは何をしなければならないのか、という動的な側面です。


動的なモデルは時間軸を含むため、単一のモデルで全体を表現することができません。したがって、ソフトウェア全体を俯瞰するためには、つまり「システムがどのようなものか」をとらえるためには、時間軸を無視して全体を一枚に表現する静的なモデルが必要となります(Copeであれば、空間的という表現を使うでしょうか)。それは、ユースケース図かもしれないし、機能構成図かもしれないし、ER図かもしれない。どのかたちに落とすのが適切かは、先行する動的モデルの性質と精度によるでしょう。静的なモデルを引くことにより、先行する動的なモデルの検証も行うことができます(「これがあるということは、こういうこともしないといけないですよね?」)。


こうして出来上がった静的なモデルは、今度は動的なモデルによって検証されることになります。静的なモデルの構成要素が登場するインタラクションを時間軸を導入しつつより具体的なレベルで分析することで、必要な要素が抜けていないか(網羅性)、構成要素に矛盾がないか(整合性)といったことを検討できるのです(「これをやるためには、こういうものも必要ですよね?」)。


このとき重要なのは、「動的」という言葉が時間軸を含んでいるということです。たとえば、「メッセージング」のような概念はそれ自体が動的であるかのように思えますが、単純に2つの構成要素間でメッセージングが行われていることを示しているだけなら、それは静的なモデルです。コンテキストを限定し、時間の流れとともに何が起きるのかを追跡するのが動的なモデルだということです。


こうして、全体像とその動き(静と動)をクロスチェックしながら、抽象的なところから具体的なところに落としていくことで、その作業の結果が設計成果物となっていくわけです。

プログラムをデザインする

こうした静と動の関係は要件定義フェーズだけに限定されるものではなく、成果物の抽象度が詳細レベルまで落ちてきても本質的には変わりません。たとえば、ER図の妥当性は最終的にはSQLを設計する際に検証される、ということです。プログラムの設計の場合にはどうかと考えると、ここにはSteve FreemanとNat Pryceの提唱している「モックを使ったTDD」がうまく当てはまります(「モックを使ったTDD」について詳しくはこちら)。


「この入力に対してはこの出力が返される」という仕様は静的なものです。モックを使ったTDDの場合、実装時にはユニットテストコードを書きながらオブジェクトとコラボレーターとのインタラクション(動的)を規定していきます。テストコードがグリーンになったら、今度はコードを静的な構造としてとらえ直し、コラボレーターとしてのインターフェイスを実装したクラスの配置をリファクタリングします。ここではTDDのサイクルが、静と動との往還と一致しているのです。

まとめ

「システムがやること」と「システムのあり方」あるいは「静/動」という概念を軸に、設計について考えてきました。最後に、ドメイン駆動設計について少し触れておきたいと思います。『エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)』の中で登場するのは、ほとんどがエンティティ同士の関係を示す静的なモデルです。しかし、これらの静的なモデルを作り上げる過程では、シナリオの中でそうしたエンティティがどう動くのかという動的な側面が、顧客との対話の中で考察されていることが読み取れるでしょう。


事実、DDDを行うプロセスとして現在エリックが整備している「モデルを探究するうずまき*2では、動的な要素としてのシナリオと、静的な要素としてのモデルとの往還関係を繰り返しながらモデルが進化していくとされています。


Copeが「アーキテクチャ」という言葉を使うときには、システムをとらえるためのこうした大きな枠組みが含まれています*3。こうしたアーキテクチャに対する思考は、アジャイルであれ、ウォーターフォールであれ、ある程度はアップフロントに行うべきものだと思うのです。

*1:にも似たようなことは書いた気がしますが

*2:以前、こちらのエントリで少し整理しました

*3:Alexanderの思想を受け継いでいる彼の場合、実際にはそれをも超えて、人とシステムとのかかわり全体(バリューストリームと呼ばれるもの)までが視野に入っているのですが

モックによるインターフェイスの発見

設計ツールとしてのモックの使い方について考える。

導入

先日、"Mock Roles, not Objects"の日本語版「ロールをモックせよ」を公開しました。この論文は2004年に書かれたもので、著者はSteve Freeman氏、Nat Pryce氏、Tim Mackinnon氏、Joe Walnes氏という豪華メンバーです。また、Steve Freeman氏とNat Pryce氏は『Growing Object-Oriented Software, Guided by Tests (Addison-Wesley Signature Series (Beck))』(いわゆるGOOS)の著者でもあり、"Mock Roles, not Object"で語られている思想はGOOSのベースになっているとも言えます。


今回は、この"Mock Roles, not Objects"(以下、MRnO)で語られているモックの基本的な考え方について、GOOSを意識しつつ掘り下げてみたいと思います。

設計手法としてのモック

まずは"MRnO"で紹介されているサンプルを元に具体的なコードを見ていきましょう(なお、このエントリで紹介するサンプルは"MRnO"を元にjMock2に移植したものです)。実装する仕様は以下の通りです:

オブジェクトをロードするフレームワークに対してキーを元にした検索を行い、その結果をキャッシュするコンポーネントを考えてほしい。ロードされてから一定時間が経つと、そのインスタンスは使えなくなる。そこで、時々リロードをしなければならなくなる。
Mock Roles, not Objects(p.7)

最初はシンプルな正常ケースを書きます。

@Test
public void キャッシュされていないオブジェクトはロードする() throws Exception {
 
    final ObjectLoader mockLoader = context.mock(ObjectLoader.class);
 
    context.checking(new Expectations() {
        {
            oneOf(mockLoader).load("KEY1");
            will(returnValue("VALUE1"));
        }
    });
 
    TimedCache cache = new TimedCache(mockLoader);
    assertThat((String) cache.lookup("KEY1"), is("VALUE1"));
}

「cache.lookup()のタイミングで、TimedCacheのコンストラクタに渡されているmockLoaderのloadが呼び出され、その際には"VALUE1"が返されるのでlookupの戻り値も"VALUE1"になる」というからくりです。重要なことですが、この時点ですでにObjectLoaderというコラボレーター(隣接オブジェクト)が発見されています。図示するとこんな感じ:

1つ補足しておくと、ここでObjectLoaderをモック化しているのは、それが外部リソース(DBなど)にアクセスしているっぽかったり、サードパーティーライブラリを想定していたりするからではありません。テスト対象のオブジェクトが動作する上で必要なコラボレーターを、テストを書きながら発見することがモックを使ったTDDの中核です。


この後、2回目に呼ばれたときにはObjectLoaderを呼び出さずにキャッシュを使うテストも当然書かなければいけないのですが、アサート文が2つになるだけですので省略します。キャッシュを実装した段階でコードはこうなっています:

public class TimedCache {
 
    final private ObjectLoader loader;
    final private Map<String, Object> cachedValues = new HashMap<String, Object>();
 
    public TimedCache(ObjectLoader loader) {
        this.loader = loader;
    }
 
    public Object lookup(String key) {
        if (!cachedValues.containsKey(key)) {
            cachedValues.put(key, loader.load(key));
        }
        return cachedValues.get(key);
    }
}

さて、ここで時間の概念を導入しましょう。仕様書にあるこの一文ですね。「ロードされてから一定時間が経つと、そのインスタンスは使えなくなる。そこで、時々リロードをしなければならなくなる。」これに対応すると、テストはこうなります:

@Test
public void タイムアウト後のキャッシュされたオブジェクトはロードする() throws Exception {

    final Clock mockClock = context.mock(Clock.class);
    final ObjectLoader mockLoader = context.mock(ObjectLoader.class);
    final ReloadPolicy mockPolicy = context.mock(ReloadPolicy.class);

    final Timestamp loadTime = new Timestamp("2011/09/17 00:00:00.000");
    final Timestamp fetchTime = new Timestamp("2011/09/17 00:00:01.000"); // 1秒後
    final Timestamp reloadTime = new Timestamp("2011/09/17 00:00:02.000"); // 2秒後

    context.checking(new Expectations() {
        {
            exactly(3).of(mockClock).getCurrentTime();
            will(onConsecutiveCalls(returnValue(loadTime), returnValue(fetchTime), returnValue(reloadTime)));

            exactly(2).of(mockLoader).load("KEY");
            will(onConsecutiveCalls(returnValue("VALUE"), returnValue("NEW-VALUE")));

            atLeast(1).of(mockPolicy).shouldReload(loadTime, fetchTime);
            will(returnValue(true));
        }
    });

    TimedCache cache = new TimedCache(mockLoader, mockClock, mockPolicy);
    assertThat("ロードされたオブジェクト", (String) cache.lookup("KEY"), is("VALUE"));
    assertThat("キャッシュされたオブジェクト", (String) cache.lookup("KEY"), is("NEW-VALUE"));
}

図に表すとこんな感じ:

モックを使ったTDDのユニットテストが通常のTDDと大きく違う点が、実はここに現れています。通常のTDDの場合、こうしたコラボレーターが登場するのは「レッド・グリーン・リファクタリング」のうち、主に「リファクタリング」のプロセスになります。まずは動くようにした上で、より適切な責務分割を目指すわけですね。


それに対して、モックを使うとコラボレーターは、テストを書いている段階すなわちレッドの手前で登場します。実際に写経してみるとわかるのですが、登場するオブジェクトとそうしたオブジェクト間のインタラクションを相当はっきりイメージできていないと、テストを書くことができません。


この意味で、モックを使ったテスト駆動開発ではテストを書くことが設計することに直結するわけです。ただし、ここで設計されるのはあくまでオブジェクト間のインタラクションであり、クラスではありません。「では、クラスはどうやって設計するのか?」という疑問が当然出てくるわけですが、GOOSを読むと、いったんはインターフェイスの匿名クラスとして実装し、あとから名前のついた適切なクラスを抽出するというスタイルがとられていることがわかります。そうして抽出されたクラスの内部で責務があいまいだと感じた場合には、再びユニットテストを書いてコラボレーターを発見するというプロセスを繰り返すことになります。

まず受け入れテストを書く

これが、このアプローチが「外から内へ」と言われる理由です。外側(=システムへのエントリポイント)に近い場所からユニットテストを書き、インターフェイスを発見しながら内側へと入っていくわけです。すると当然、「最初のテストはどこから書き始めるのか」という疑問が生じると思いますが、それに対する答えは「まず受け入れテストを書く」ということになります。全体を図示するとこんな感じでしょうか:

実際には、受け入れテストをグリーンにする過程で必要に応じてユニットテストを書きつつインターフェイスを発見し、グリーンになった後も責務があいまいなクラスに対してまたユニットテストを書き、という具合にループが2重になっています。一見、受け入れテストの話とユニットテストの話は別々のものであるようにも見えます。しかし、モックを使ったユニットテストが、オブジェクト間のインタラクションという内側にフォーカスしている分、システムが全体として正しく動いているかという外側を明示的に意識する必要があると考えれば、両者をまとめて1つのプロセスとする考え方は筋が通っていると言えるでしょう。「受け入れテスト+モックを使ったユニットテスト」をセットで考えれば、ユニットテストを書くという行為自体が従来のリファクタリングフェーズに当たると考えることができるかもしれません。


なお、受け入れテストから始めるというこの考え方は、ふるまい駆動開発(BDD:behaviour-driven development)とも強い親和性があります。すなわち、「システムが何をすべきかをテストで表現する」ことにより、どこから始めればよいか、どうすればそのフィーチャの実装が完了したと言えるのかという問いに同時に答えることができるのです。

最後に

プログラマにとって、コーディングをしながら構造を発見していくプロセスというのはかなりエキサイティングに感じられるものですが、現実問題として本当に何もないところからこれを始めるのはいつになったら終わるのか全然わからないという怖さがあります。したがって、ある程度は事前に分析/設計を行い、大まかな構成のメドを立てておく必要があるということになります。よく批判される"Big Design Up Front"を避けつつも、事前の分析/設計を適切に行うバランス感覚が重要だということですね(それが簡単にできれば苦労はしないという話ですが)。


モックに興味を持たれた方は、ぜひ「ロールをモックせよ」を読んでみてください。ここで取り上げたこと以外にも重要な示唆が色々と書かれています。また、サンプルコードはこちらで公開しています。