Elasticsearch でつまづいた話 (3)

今回のテーマはページング取得。

Query したときの件数

RDB の発想だと SQL select * from table したら全件取得されるのが当然だが Elasticsearch だと何も指定しなければ 10 件までしか取得されない。

size を指定するとこれを大きくできる。

{
   "size" : 100,
   "query" : { ... }
}

だが、この size にも上限がある。デフォルトだと 10,000 件だ ( _settings の index.max_result_window で定義されている) 。

作法としては Scroll API というのを使う。これは、ある時点でのスナップショットを保存して、取得しきれなかった分を辿っていくというものだ。

( From/Size でやろうとすると追加/削除があった場合に過不足が発生するので良くない)

クエリ文字列として scroll=hoge を渡す。 hoge1m など、スナップショットを保持する期間を指定する。

curl -XGET '[host]/_search?scroll=1m' -d '
{
   "size" : 100,
   "query" : { ... }
}'

レスポンスは以下のようになる。

{
   "_scroll_id" : "hogehoge",
   "hits" : { ... }
}

_scroll_id があるということは、取得しきれなかった分があることを意味する。続きを取得するには、以下のリクエストを送る。このときクエリを指定する必要はない。

curl -XGET '[host]/_search?scroll' -d '
{
   "scroll_id" : "hogehoge"
}'

このレスポンスにも続きがあれば新たな _scroll_id が返ってくるので、それを使って再び叩くことになる。

取得が完了したら、 DELETE でスナップショットを削除すると行儀がいい。

Aggregation 時もページングが必要になる場合があるが、その際はまたちょっと違った API を使うことになる。その話はまた今度。

ASP.NET のセッションと同時実行

ASP.NET で複数リクエストからセッションを扱おうとすると困ったことになるよという話。

SPA とかだと、画面からいろんなリクエストを非同期で飛ばして、パーツごとに表示することがある。 そんなとき、たとえば認証情報をセッションから取得して、その情報で DB を見に行き、取得内容を返す、といったことが考えられる。

[HttpGet]
public ActionResult Company(string id) {
   var loginInfo = Session[id];
   var company = GetCompany(loginInfo.CompanyId);

   return Json(company, JsonRequestBehavior.AllowGet)
}

[HttpGet]
public ActionResult User(string id) {
   var loginInfo = Session[id];
   var user = GetUser(loginInfo.UserId);

   return Json(user, JsonRequestBehavior.AllowGet)
}

こんなとき AjaxCompanyUser を呼ぶと、後に呼ばれたリクエストは先に呼ばれたリクエストの response が完了するまで待たされてしまう。

これは .NET が Session をロックしているためだ。確かに同時実行中に書き換わってしまったら困るのだが、パフォーマンス的にはそれでは困る。

回避するためには、セッションに対して書き込みは行わないよ!ということを属性で明示する。

[SessionState(SessionStateBehavior.ReadOnly)]
HomeController : Controller {
   ...
}

残念なことに SessionState 属性は Action ではなく Controller 単位にしか付与できない。

正解率の高い予測が必ずしも良い予測ではない

タイトルのとおりなんですが、何かしらの予測に対してその良さを語るために「正解率90%」とか「精度90%」とかよく表現されますね。

で、今回は「正解率が高ければ良いというわけじゃない」という話です。

正解率 (accuracy)

正解率の定義は、当然、正解数を問題数で割った数値です。

 \displaystyle \frac{正解数}{問題数}

予測を評価するための数値として、きわめて真っ当に見えます。しかし、必ずしもそうではないのです。

コンピューターウィルスを予測する

「PC 内のファイルがコンピューターウィルスかそうでないか」という問題を考えてみましょう。

通常、 PC 内のファイルはほとんどがウィルスではありません。仮に、 0.01 % のファイルがウィルスだったとしましょう(かなり多いですが)。 そうすると、「全てのファイルはウィルスではない」という予測ですら、正解率は 99.99% になってしまいます。

数値は高いですが、これでは何の意味もありません。

ではこのとき、どういった数値をみる必要があるのでしょうか?

適合率 (recall)

コンピューターウィルスを予測するときは、ウィルスでないファイルをウィルスであるという誤検知が多少あったとしても、ウィルスであるファイルを確実にウィルスである、と予測したいでしょう。

 \displaystyle \frac{正しくウィルスであると予測できた数}{実際のウィルスファイル数}

正解率との違いがわかるでしょうか?表現を変えると以下のようになります。

 \displaystyle \frac{実際にウィルスであり、ウィルスだと予測できた数}{実際にウィルスであり、ウィルスだと予測できた数 + 実際にウィルスだが、ウィルスだと予測できなかった数}

このような尺度を適合率 (recall) と呼びます。

精度 (precision)

話は変わります。冒頭で、「正解率90%」と並べて「精度90%」と表現しました。実は、精度 (precision) という尺度が正解率と別に定義されています。

 \displaystyle \frac{実際にAである数}{Aであると予測した数}

さきほどのコンピューターウィルスの例を使いまわすと、以下のようになります。

 \displaystyle \frac{正しくウィルスであると予測できた数}{ウィルスだと予測し、実際にウィルスだった数 + ウィルスだと予測し、ウィルスではなかった数}

適合率と精度

さて、コンピューターウィルスを予測する際は適合率を高くしたいという話がありました。しかし、ここでも罠があります。やはり、「全てのファイルがウィルスである」という予測をすると、適合率が100%になってしまいます。確かに全てのコンピューターウィルスを検知できたのですが、これでは役に立ちません。

そこで、適合率と精度を組み合わせるという手段があります。

こんな表を用意します。ここで T : True (実際に正解), F : False (実際に不正解), P : Positive (正解と予測), N : Negative (不正解と予測) を意味します。

実際は正解 実際は不正解
正解と予測 TP FP
不正解 TN FN

ぱっと見、なんだかややこしい気がしますが、さきほどのコンピューターウィルスの例と同じです。

 適合率 = \displaystyle \frac{TP}{TP + TN}

 \displaystyle \frac{TP}{TP + FP}

これのちょうどいいバランスをとりたいわけです。例えばこんな感じだとどうでしょう。

 精度 = \displaystyle \frac{2 \times 適合率 \times 精度}{適合率 + 精度}

これは調和平均と呼ばれる平均の一種です。普通の平均だとダメなの?という疑問が湧くと思いますが、今回は割愛します。

この数値自体は F 値 (F-measure) と呼ばれます。

まとめ

というわけで、「正解率が高ければ良いというわけじゃない」という話でした。

しかし、「F値こそが正義!」というわけでもありません。単純に適合率と精度の真ん中らへんをとっているというだけですから、実際はどのあたりのバランスが「現実的に嬉しいのか」を考える必要があります。コンピューターウィルスの例では、おそらく、精度よりも適合率の方を重視した尺度を用意すべきように思います。

また、他にも ROC curve だったり AUC だったり、今回みた以外にもいろいろな尺度があります。奥が深いですね。

専門家ではないのでこのへんで。

いろいろな交響曲第1番

作曲家にとって交響曲第1番はロックバンドにおける 1st アルバムと同じ。 Oasis の Definitely Maybe だし、 Weezer の blue album だ。 今回は、偉大なる作曲家たちの交響曲第1番を振り返ってみる。セレクションは適当です。

L. v. ベートーヴェン

www.youtube.com

1st アルバムとか言っておきながら、ベートーヴェン交響曲第1番を発表したのは彼が30歳のとき。満を持しての交響曲である。

冒頭、ハ長調というど真ん中の調のくせに、属七のヘ長調から始まり、4小節目でようやく五度のト長調になる。異常だ。 そして10小節目でハ長調とみせかけてのト短調。序奏からしてもう名曲だ。スピード感あふれる第1主題も最高。展開部の変奏も楽聖としか言いようがなさすぎる。 第3楽章のスケルツォメヌエットということになっているが、実質スケルツォ)、私は古典派のスケルツォ楽章は退屈でしょうがないのだが、この裏拍感が支配するダイナミックな音楽、現代のロックバンドに見習ってほしいほどである。

F. J. ハイドン

www.youtube.com

交響曲の父ハイドン。彼は生涯で 104 番 (番号なしを含めると 108 曲らしい) までの交響曲を生み出した。 一聴してわかるとおり、完成度が高すぎる。どうやら彼が本当に最初に作曲した交響曲ではないらしい。 104 番まで作っておきながら、捨て曲が無いというのがありえない。

P. I. チャイコフスキー

www.youtube.com

最も人気のあるシンフォニストのひとり、チャイコフスキー。 彼の交響曲第1番は標題つきであることもあって演奏される機会もそこそこあるが、正直、地味だし演奏効果も低いと思う。 だが、たとえばフィナーレ弱奏部の煽り方など、所々に「こいつ、もしかしたら凄い作曲家になるのでは??」という期待感がある。 Happy Mondays の 1st のような、 Number Girl の SCHOOL GIRL BYE BYE のような、改めて聴くと大事にしたくなるような感じだ。

A. ドヴォルザーク

www.youtube.com

神が作ったかのような交響曲第9番新世界より」は、史上最も完成度の高い交響曲であると認める人も多い。 そんなドヴォルザーク交響曲第1番、私は今回初めて聴いた。 まぁ...ちょっと...つかみどころが無いかな... 内声の充実度は当時からあったみたいね...

G. マーラー

www.youtube.com

マーラーは近代作曲家のなかでも最も人気のある人物といっていいだろう。 冒頭の宇宙的和音(ユニゾンだけど)、第2楽章の耽美なワルツ、既に彼の世界観が確立されている。ムカつく。フィナーレの大勝利感。何がベルアップだよ。最高だ。

J. シベリウス

www.youtube.com

私が最も好きな作曲家、シベリウス。世間の評価でいうと、後期の第4番以降の作品は洗練されていて完成度が高いといわれる。最もよく演奏される交響曲は第2番である。 (クレルヴォ交響曲という番号なしの大交響曲があるが、今回は無視する)

そんな彼の第1番は、初期衝動そのもの。これが俺の音楽だ。ロマンティシズムだ。たとえば交響曲第6番と比べると、全く別人の音楽である。 「シベ1は後期の作品と比べると音楽性が低いよね 笑」なんて言う人もいるが、この美しい第4楽章第2主題の前では、そんな戯言に価値は無い。


ブラームスどうしたよ!とか、交響曲といえばセーゲルスタムだろ!(そんな人いるか?)とかの声もあると思うが、今回はこのあたりで。

Elasticsearch でつまづいた話 (2)

今日のテーマは複数の Nested Field があるときの Aggregations 。

Nested Field とは

日本語での説明は このあたり に譲るが、簡単に言うと親子関係が維持された Array である。データの見た目は Array と同じだ。

  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]

クエリはこんな感じになる。

{
   "query" : {
      "nested" : {
         "path" : "user",
         "query" : {
            "match" : {
               "user.first" : "John"
            }
         }
      }
   }
}

Array だったら↓で済むので少し複雑になっている。

{
   "query" : {
      "match" : {
         "user.first" : "John"
      }
   }
}

Aggregation ならこうなる。

{
   "aggs" : {
      "nested_by_resellers" : {
         "nested" : {
            "path" : "resellers"
         },
         "aggs" : {
            "aggs_by_resellers_id" : {
               "terms" : {
                  "field" : "reseller.id"
               }
            }
         }
      }
   }
}

Nested Field が複数ある場合もある。

{
  "mappings": {
    "product" : {
        "properties" : {
            "resellers" : { 
                "type" : "nested",
                "properties" : {
                    "id" : { "type" : "keyword" },
                    "name" : { "type" : "text" },
                    "price" : { "type" : "double" }
                }
            }, 
            "sources" : {
               "type" : "nested",
               "properties" : {
                  "id" : { "type" : "keyword" },
                  "name" : { "type" : "text" }
               }
            }
        }
    }
  }
}

このとき resellersid で集約し、更に sourcesid でも集約したい場合どうするか。 これが今回のつまづいた話だ。 たとえば↓のように書きたくなるが、これではうまくいかない。

{
   "aggs" : {
      "nested_by_resellers" : {
         "nested" : {
            "path" : "resellers"
         },
         "aggs" : {
            "aggs_by_resellers_id" : {
               "terms" : {
                  "field" : "reseller.id"
               },
               "aggs" : {
                  "nested_by_sources" : {
                     "nested" : {
                        "path" : "sources"
                     },
                     "aggs" : {
                        "aggs_by_sources_id" : {
                           "terms" : {
                              "field" : "sources.id"
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

なぜか? reseller の下に sources がいないからだ。そりゃそうだ。

じゃどうするか。 reverse nested というものを使う。これによって nested を抜け出すことができる。

{
   "aggs" : {
      "nested_by_resellers" : {
         "nested" : {
            "path" : "resellers"
         },
         "aggs" : {
            "aggs_by_resellers_id" : {
               "terms" : {
                  "field" : "reseller.id"
               },
               "aggs" : {
                  "reverse" : {
                     "reverse_nested" : {},
                     "aggs" : {
                        "nested_by_sources" : {
                           "nested" : {
                              "path" : "sources"
                           },
                           "aggs" : {
                              "aggs_by_sources_id" : {
                                 "terms" : {
                                    "field" : "sources.id"
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

reverse_nested を挟むことによって、 resellers の下にいた状態から抜け出して、更に sources の下に入ることができた。 めでたしめでたし。

... とはいえ、複雑な json だよなぁ。

参考 :

Reverse nested Aggregation | Elasticsearch Reference [6.0] | Elastic

Elasticsearch でつまづいた話 (1)

一人で Advent Calendar をやる。

テーマは決めてない。技術か音楽の話が大半になりそうだ。


いまのプロジェクトで Elasticsearch を使っている。そのなかで、知ってたらそりゃ当然なんだけど、はじめ概念として理解できなかったことが色々あったのでまとめてみる。タイトルに (1) とつけたが、続くかどうかは未定だ。

なお私が Elasticsearch の仕様と Lucene の仕様を混同している可能性がある。

Elasticsearch の概要

Elasticsaerch は全文検索エンジンであり、基本的に REST API によって利用する。

ドキュメントが大量にあっても高速に検索できることが特徴のひとつだ。

今回のつまづいた話

Elasticsearch はデータを保存する際、必ずしも入力された文字列をそのまま保存しない。フィールドに tokenizer/analyzer が指定でき、例えば日本語用のものを使えば文章を形態素解析して保存したりできる。

ここで注意したいのが、 token に分割されたフィールドはもとの文字列がもはや保存されていないということだ。つまり、「お待ちしております」が「お,待ち,し,て,おり,ます」と分割されたら、完全一致検索 (term query) で「お待ちしております」で検索しようとしてもヒットしないことになる。これは aggregation なども同様の問題が起こり、集約のキーにしたいのに分割された文字列がキーになってしまったりする。

文章だと完全一致で検索したいことは少ないかもしれないが、名称で検索する場合は、表記ゆれも tokenizer/analyzer で吸収したいし完全一致でも検索したい、ということが結構ある。

ここで登場するのが Multi Field だ。これをスキーマ定義に指定すると、ひとつのフィールドに入力された文字列が複数の方式で analyze される。 たとえば↓のように定義されていると、 hoge.piyo を用いると tokenize されたフィールド (text) が利用でき、 hoge.poyo を用いると tokenize されないフィールド (keyword) が利用できる。

"hoge": {
  "type": "multi_field",
  "fields": {
    "piyo": {
      "type": "text"
    },
    "poyo": {
      "type": "keyword"
    }
  }
}

また↓のようにも定義できる。この場合は hoge で text、 hoge.fuga で keyword が利用できる。Elasticsearch 5.0 以降では自動でこのマッピングが生成されるらしい。

"hoge": {
  "type": "text",
  "fields": {
    "fuga": {
      "type": "keyword"
    }
  }
}

hoge で定義したフィールドに対して更に下のフィールドを利用する、というイメージが当初全然理解できなかったというのがつまづきポイントであった。

ユーザーストーリーマッピング

ユーザーストーリーマッピングを読みました。

ユーザーストーリーマッピングの方法論よりも、とにかく会話する、作るものを減らすことを論じています。 ドキュメントが正義じゃない、バックログを上から消化するのが正義じゃない、というのは私もぼんやりと感じていたことではありましたが、よりはっきりとその感覚が着きました。 ストーリーマッピングを実際に運用するには、本文にもありましたが、外部コーチに依頼するのがよいでしょうね。

以下まとめ。基本的に丁寧語は私の意見とか解釈です。

0章 まず最初に呼んでください

共有ドキュメントは共通理解ではない

  • 本書の中で何度も登場する概念ですが、目的だったり手段だったり、どんな対象であっても、共通理解を得るためには会話が必須である、ということです。「顧客が本当に必要だったもの」の風刺絵は有名ですが、「共通理解を得られていない」状態は本当に容易に発生してしまいます。これを防ぐためにはドキュメントでは足りません。多くのエンジニアは体験していることでしょう。
  • ドキュメントは共通理解を思い出すためのツールである。同時に、共通理解を思い出すためのツールとして、会話の際の写真だったりビデオにまとめたりすることも有用である。

大切なのはソフトウェアではない

  • ソフトウェア(アウトプット)を作ることは目的ではない。本当に必要なものは、アウトプットの後に結果として現れるもの(成果)だ。
  • いつも私たちが持っている時間とリソース以上に、作るべきものが存在する
    • 「作るべき」という表現が曖昧です。私は「作ることになっている」といったほうがしっくりきます。
    • やるべきことは、「作ることになっている」を減らすことだ。アウトプットを最小化し、成果を最大化することだ。

「要件」

  • 要件という言葉は危険だ。「これは要件である」と言った瞬間、思考は停止する。

1章 全体像

筆者の不満からストーリーマッピング

トーリーマッピングの流れ

  • 前提: 顧客とエンジニアが会話しながら進める。会話の内容はカードやポストイットに記入する。
  • 始めは要件リストを作ることを目的としない。ビジネス目標を明らかにすることだ。そのビジネスによってどんな利益がもたらされるか、どんな課題が解決されるか、カードに記入して優先順位をつけて並べる。
  • 次にユーザーのイメージを描く。どんなユーザーがいて、それぞれの現在と理想の状態をカードに記入する。これも優先順位で並べる。
    • アウトプットを減らすことを忘れてはならない。すべてのユーザーを対象にできないことに気をつける。
  • ユーザーのストーリーを描く。横軸を時間軸として、そのユーザーがどんな体験すをするのかを記入していく。
    • カードを並べることでコミュニケーションができる、ということが面白さだ。
    • 時系列に並べることで、思考の漏れに気づくことができる。そのためにもこの時点では深さよりも幅を重視する。
  • 各ストーリーの各ステップを詳細化し、下にカードを並べていく。

2章 作るものを減らすためのプラン

  • 1章で全体像を作ったが、すべてを作ることを目的にしてはならない。

システム内部の機能を決めるためには、まずシステムの外側で起きて欲しいことを考えよ

  • 私にとって、シンプルでありながら良く響く、いい言葉です。

トーリーマップのスライス

  • トーリーのカードが横方向で時系列順に、縦方向に優先度順で並んでいる。ここで、リリースの単位を区切るため横にテープを貼る。最も上のテープが MVP を示す。
  • 優先度は機能ではなく成果で決める。

MVP

  • Minimum Viable Product。いまや一般的に用いられる言葉ですが、筆者は viable という言葉について、我々の向き合い方が足りないことを懸念しています。 viable とは「自力で生き残れる」という意味。作るものを減らすため、我々には問いが必要ですが、その問いとは、どうであればユーザーにとって minimum で viable であるか、ということです。
    • MVP とは、望まれる成果を実現できる最小の製品のリリースである。
  • 筆者は MVS (Minimum Viable Solution) という言葉を好んでいます。我々が作るものは全く新しい製品であることよりも、既にあるものに機能や能力を追加することが多いためです。
    • MVS とは、望まれる成果を実現できる最小のソリューションリリースである。
  • minimum で viable かどうかはリリース前は推測にすぎない。仮定を証明または反証される必要がある。

3章 より早く学ぶためのプラン

  • 何を学ぶか?それはどうだったら minimum かつ viable か、です。
  • 取り組もうとしている問題が本当に存在するか。私たちは時が経つと問題の実在性が頭から抜けていきますが、定期的に向き合うべきものでありましょう。
  • 紹介されている例では、 viable ですらない製品を開発し、そこから機能を追加していく、という方法を採っていました。まさに、いかに作るものを減らすか、ということにフォーカスした方法です。

4章 時間どおりに終わらせるためのプラン

  • マップは製品全体のものを作る必要はない。既にある製品に機能を追加する際は、その機能のマップを作成しても十分だ。
  • よい見積もりには共通理解が不可欠だ。

マップのスライス

  • マップは3つにスライスする。1つ目は特定のストーリーを最初から最後までつなげるもの。2つ目は他のストーリーをカバーする(1つ目のスライスの不確実性を補う)もの。3つ目は機能を磨けるだけ磨くもの。
  • 各スライスはリリースの単位ではない。イテレーションを回し、どのスライスまでをリリースするかを判断する。

ダヴィンチに学ぶ

  • 「偉大な芸術に完成はない。あるのは途中で投げ出されたものだけだ。」 – レオナルド・ダ・ヴィンチ
  • ダヴィンチがモナリザを5日間で描くとして、キャンバスを5等分し、それを1日に1区分ずつ完成させていく、なんてことはしなかっただろう。描く前のイメージが完璧ではなく、描きながら学習する必要があることを彼は知っていたはずだ。
  • 我々の開発も同じく、スケッチのような基本機能のシンプル版を作り、イテレーティブに学習しながら追加していく。

5章 あなたはもうやり方を知っている

  • マップを作る練習として、朝起きてから家を出るまでの自分のストーリーマップを作る、というものです。

起床時のストーリーマップ

  • まず今朝起きてから行ったことをすべてカードに書く。
    • この時点で粒度は気にしない。だいたい15-25になる人が多い。
  • カードをサマリーレベルにまとめる。
    • 例えば髪を洗う、お湯の温度を調整する、などはシャワー浴びる、というタスクに。
  • カードを横軸方向に、時系列順に並べる。
    • ナラティブフローと呼ぶ。
  • カードを眺めて、見落とした部分があったら埋める。
  • 別のストーリーを探る。
    • 今朝はいま描き出したストーリーだったかもしれないが、たとえば昨日の朝はどうだっただろう。いつもより遅く起きて慌てて準備したりしなかっただろうか。
  • フローを整理する
    • 緊急時の朝、通常時の朝、素晴らしい朝などで分ける
  • アクティビティを整理する
    • 共通の目標があるタスクを集約したものをアクティビティと呼ぶ。かばんを持ち、天気をチェックし、必要なら傘を持つといった行為。名前がついていない事が多いが、何かしら呼び名をつける。
  • タスクをスライスする
    • 寝坊した場合、すべてのタスクをこなすことはできない。その場合の最小のタスクをスライスする。

実際のストーリーマップ

  • 前述のストーリーマップは「現在の」マップだ。製品開発の際は「将来の」マップを作る必要がある。しかし本質は変わらない。

6章 ストーリーについての本当のストーリー

  • トーリーは作ることが目的になってはいけない。語られなければならない。ケント・ベックも言っている。

7章 より良いストーリーテリングのために

  • トーリーを語るためのコツ。

トーリーのテンプレート

  • テンプレートを使用すると取り組みやすい。
    • [ユーザータイプ]として、[これこれの結果を得る]ために、[これこれを]したい。
  • ただし、テンプレートが目的になってはいけない。これはストーリーを語るための道具にすぎない。
  • 誰が、何を、なぜ、をリアルに語ろう。

8章 カードに書かれていることがすべてではない

  • プロダクトには多くのステークホルダーが存在する。交わされた会話はカードに書き出し、写真で保存しよう。
  • ツールを導入したがるケースが多いが、たいていホワイトボードとカードで行ったほうがうまくいく。

9章 カードは始まりにすぎない

  • ケント・ベックは、2011年にアジャイル宣言の修正版を発表した
    • 動くソフトウェア(または包括的なドキュメント)よりも、「検証された学習」を。
  • これは知りませんでした。「検証された」というのは、作った結果について考えるという意味が込められているそうです。イテレーティブということがここでも効いてきます。

10章 ケーキのようにストーリーを焼く

  • 大きいストーリーは分割しよう、という話。

11章 岩を砕いていく

  • トーリーは、扱う人によって適正サイズが異なる。
    • ビジネス -> ユーザー -> 開発
    • それでも、岩をどれだけ砕いても岩であるのと同じように、ストーリーはどれだけ分割してもストーリーだ。
    • この語彙を分けたくなるのはソフトウェア業界の人間の性だが、あまりこだわらない方が良い。それよりも語ることを考えよう。

12章 岩を砕く人

  • トーリーの責任者はプロダクトオーナーだけである、というのは誤解だ。
  • 我々が求めるソリューションは、価値がある、使いやすい、実現可能である、の3つの円が重なりあう部分にある。それぞれを見極めるために様々なスキルをもった人々が協力しなければならない。

13章 オポチュニティから始める

  • 分割する元のストーリーを、本書ではオポチュニティと呼ぶ。エピックと呼ぶ人もいる。
  • オポチュニティが生まれたら、それを掘り下げるべきか、ボツにするべきか、先送りにするかを検討する。
    • ボツになるのは素晴らしいことだ。作るものを減らすことを忘れてはならない。

14章 ディスカバリーを介して共通理解を築く

  • オポチュニティからプロダクトバックログを取り出す工程をディスカバリーと呼ぶ。
    • たぶん。文中で明確に定義されていないので……
  • ディスカバリーは4つのステップから成る。
    • ビジネスの立場からアイデアの枠組みを作る
    • 顧客とユーザーを理解し、どのように彼らを助けるかを明らかにする
    • ソリューションのイメージを描く
    • MVS は何か、その MVS をどう作ればいいかを明らかにするために、最小化とプランニングを行う
  • デザインスタジオ(ワールドカフェに似ている気がします)などの手法を用いて会話、アイデア創出する。
  • 優先順位付けは、ビジネス目標を考えてから。

15章 ディスカバリーによる検証された学習

私たちはほとんどの場合間違っている

  • Chaos レポートなどで報告されているとおり、作ったものが大きなインパクトを与えたというプロジェクトは全体の20%に過ぎない。64-75%は使用されてすらいない。
  • CEO が考えだした素晴らしいアイデアを実現してリリースする、なんてのは古き悪しき時代のやり方だ。

デザイン思考

  • 共感
    • 顧客として一緒に仕事してみる
  • 定義
    • 取り組む問題を定義する
  • イデア創出
    • ソリューションを複数考える
  • プロトタイプ
    • 動くものを作る
  • テスト
    • バグを見つけるテストではなく、作ったものが顧客の問題を解決するかのテスト

推測の扱い

  • 取り組もうとしていることのなかにどんな推測があって、そのリスクがどの程度かを分析する。また名前をつける。
  • 推測をストーリー形式で表現する。
    • どんなユーザーがどんな行動をとるか
  • ユーザーテストし、考え直す

16章 リファイン、定義、構築

  • トーリーマップができたが、細かいところが大雑把に見えることがある。そんなときはワークショップでストーリーに磨きをかける。
  • 私たちは具体的に何を作るのか、にフォーカスする。
    • スクラムでいうスプリントプランニングにあたる。
  • トーリーの分割もここで行われることがある。常にではない。
  • メンバーはオプトイン。全員が参加する必要はない。
  • フィッシュボウル・コラボレーションの形式もよい。

17章 ストーリーは実際にはアステロイドに似ている

  • トーリーを細かくしすぎるのも良くない。
    • 優先順位づけが大変だったり管理しづらかったりするためのようです。
  • 細かくしすぎたストーリーを戻すことはできる。

18章 構築するすべてのものから学ぶ

  • レビュー、振り返りの話
  • 製品、当初建てた計画、実行したプロセスをチームでレビューする
  • チーム外のステークホルダーに製品をレビューしてもらう
  • ユーザーテストをする
  • リリース後に加えた改良はもっとも価値がある

19章 終わり?それとも–

  • 良いソフトウェア製品と同様に、この本に終わりはない。