それじゃあ価値はどこからくるの?

前回の続きです。

Gerald Weinbergは、「品質」という言葉を「誰かにとっての価値」と定義した。高品質のデリバリを達成するには、「価値を届ける相手は誰か」、そして「デリバリに求められる価値は何か」といった理解が必要だ。

(『インパクマッピング インパクトのあるソフトウェアを作る』(ゴイコ・アジッチ、翔泳社、2013年) p.07 アクター)

もちろん、ストーリーを動くソフトウェアという形にしてユーザーに提供すれば、ユーザーはそれを使って何らかの便益を受けられます。すなわち、ユーザーは価値を得られます。こう考えれば、「価値がある」ストーリーには、文字通りユーザー価値があることになります。

しかし問題はそう単純ではありません。例として、ショッピングサイトを考えてみます。ストーリーは「商品カタログを見る」「注文する」「決済する」「発送する」などになるでしょう。すぐに典型的な疑問がわいてきます ―― 「注文する」ストーリーには単独で価値があるのか?すべてそろわないと価値がないのではないか?

ユーザーストーリーマッピングはひとつの答えになります。ユーザーが買い物をするという行為を完結できる一連のストーリーを選び、それを最小限の時間やコストで実現できる組み合わせにして、最初のリリースを定義する(これを私はMVP(Minimal Viable Product)とも呼んでいますが、人により解釈が異なるようです)。こう考えると、個々のストーリーの価値は問題ではなく、リリースというひとまとまりの動くソフトウェアに価値があることがわかります。

前回紹介した、ストーリー1つに独立して価値があるという考え方もあります。いまの時代にショッピングサイトを作るとしたら、他社にない特別な優位性が求めらるはずです。そうした優位性のアイデアが「注文する」ストーリーに込められているなら、単独で価値を持つと考えられます。リーンスタートアップのアプローチであれば、この優位性(の仮説)を検証するために、集中して実験することになります。注文の部分だけソフトウェアで実現して、ターゲットユーザーに試してもらうかもしれません。

プロダクトの強みとなる箇所だけを取り出し、検証する作業に当たります(これをMMF(Minimal Marketable Feature)と呼んでよいと思いますが、人により解釈は異なるようです)。最終的にプロダクトとしてユーザーに利用してもらうときは、一連のストーリーが必要になります。ギルドワークス株式会社の市谷さんは「学びを得ることと価値提供を混同しない」と言っています。

ユーザーストーリーマッピングに限らず、アジャイルなソフトウェア開発では、リリース計画やロードマップが用いられます。いずれも、顧客に動くソフトウェアを届け、価値を提供するための計画です。どういうタイミングで、なにを提供すれば、届ける価値を最大化できるのか。

リリースからはさらに、生のユーザーフィードバックを得られるようになります。こうしたフィードバックをどうやって得ていくかも、リリース計画の一部となります。フィードバックは様々な種類があり、ユーザー行動分析もそうですし、クチコミの評判もあれば、市場や競合の変化もそうです。売上やマーケットシェアの変化もフィードバックとなります。プロダクトの性質により、リリースが何スプリントに一度の場合も、1スプリント内に何度もリリースする場合もありますが、基本的には変わりません(間隔の長いリリースのほうが、計画が難しくなる傾向はあるようです)。

こうなると、次のリリースの内容は、その先のリリースまで見据えたものになってきます。そのため、リリース計画においてはその先のリリースも含めた、ロードマップの検討と更新が欠かせないものになります。ロードマップはプロダクトビジョンの実現、今後のプロダクトの向かう先を示すもので、チーム全体の方向をそろえる道具のひとつとなります。

ユーザーストーリーの価値

良いユーザーストーリーの条件として、よくINVESTが挙げられます。(参考: https://blog.guildworks.jp/2015/06/03/how_to_deal_with_invest_of_userstory/ )

  • Independent 独立している
  • Negotiable 交渉可能である
  • Valueble 価値がある
  • Estimable 見積り可能である
  • Sized Right (Small) 適切な大きさである(小さい)
  • Testable テスト可能である

この中のValuableについて考えてみます。ユーザーストーリーは価値がなければいけない。1個1個のユーザーストーリーに価値があれば、プロダクトオーナーは依存関係を意識せず並べられ、開発者はテストを含め完全に完成させられ、顧客は毎スプリント価値あるアウトプットを受け取れます。価値があるユーザーストーリーとは素晴らしい。

本当でしょうか。本当に、ユーザーストーリーは「価値があるべき」であり、「価値を持てる」のでしょうか。

ユーザーストーリーを扱う上で、よくある悩みは「1スプリントで完成できない」というものです。スクラムマスターは「それではストーリーを分割しましょう」と言います。Richard Lawrenceによればストーリー分割には以下のようなパターンを使えます。

  • 原則
    • 優先順位が違うものを分ける
    • だいたい同じ大きさ(小ささ)に分ける
  • パターン
    1. ワークフローのステップごとに分ける
    2. ビジネスルールのバリエーションで分ける
    3. 最初の1個目で基本形ができるように分ける
    4. シンプルな部分と複雑な部分に分ける
    5. データのバリエーションで分ける
    6. データ入力の方法で分ける
    7. パフォーマンスを別に切り出す
    8. 操作(CRUD)で分ける
    9. スパイクを切り出す

いずれももっともですし、効果的な分割ができそうです。でも分割したものには価値があるのか?あるとしてどのくらいなのか?測る方法はあるのか?

そもそも価値とはなんなのか。

エクストリームプログラミング入門の第1版で、ケント・ベックはこう書いています。

第15章 計画戦略 計画ゲーム (p.89):

計画ゲームのゴールはチームが生産するソフトウェアの価値を最大化することである。

計画ゲームは3週間ごとにおこなうとされています。常に、ソフトウェアの価値を最大化するよう計画すると述べているものの、ストーリー1つひとつに価値があるという言及は、この本には見つかりません。

アジャイルマニフェストアジャイル宣言の背後にある原則の1番目は「顧客満足を最優先し、価値のあるソフトウェアを早く継続的に提供します」とあります。ここにも、継続的に提供するのは価値のあるソフトウェアであって、ストーリーとか、機能とか、フィーチャーとかPBIとかいったものが個々に価値があるとは言っていません。

エクストリームプログラミング(第2版)では、こうなっています。(訳は筆者)

Chapter 7. Primary Practices - Stories

顧客が理解できる機能の単位を使って計画する。……見積りにより、ビジネス側と技術側の観点から相互に対話する機会が生まれ、そのおかげで価値を早期に、一番ポテンシャルが高い時期に作り出せる。コストがわかれば、その機能の価値を鑑みながら分割したり、結合、拡張することもできるようになる。

ここでは、ストーリーは相互の対話、インタラクションのためにあると言っています。ユーザーストーリーの3Cにもあるように、ストーリーに会話は不可欠です。

ここに、ストーリーが「価値がある」意味が出てきます。価値とは、それがなんであれ、誰のためであれ、ストーリーに背景を与えます。技術側はその背景を理解して初めて、ソフトウェアの価値に貢献できるようになります。技術側は正しいものを作るために、ビジネス側の観点を持たなければならない、そのための「ストーリーの価値」です。

アジャイルマニフェストの原則ではまた、「できるだけ短い時間間隔でリリースします」「ビジネス側の人と開発者は、プロジェクトを通して日々一緒に働かなければなりません」とあります。ここから、ビジネス側の人、ひいては顧客側は開発者にフィードバックをするチャンスが得られます。ストーリーに価値があれば、その部分をさわって、満足か不満か、直すべき点はどこかという情報を開発側は受け取れます。開発者の知識とソフトウェアの実装は一致しています。そこにビジネス観点から見て間違いがあれば、開発者の知識を修正でき、ソフトウェアも正しくなります。そして「チームが生産するソフトウェアの価値を最大化する」ことに近づきます。

以上から、ユーザーストーリーが価値があるのは、正しいもの、価値あるソフトウェアを作るためと言えます。そうなる確率を上げるために、作る前の会話を効果的にし、作った後の確認も容易になります。これはすなわちリスクマネジメントの手法と言えます。価値があるユーザーストーリーを書くと、ソフトウェアが失敗するリスクを下げられる。

ストーリーに価値があるのは、実現したらソフトウェアの価値がそのぶん増すというような、単純なものではないのです。リスクマネジメントの手法なので、どんなリスクを想定するか、どんな情報を得たいか、そのうえでどれを優先すべきか、考えるための道具として使うものです。「間違ったものを正しく作る」事態を避ける道具のひとつなのです。

(まだ十分に考えが整理されていないのですが、いったんまとめるために本稿を書きました。観点の不足もあると思うのですが、コメントなどで突っ込んでもらえると嬉しいです。)

年末にThinkPad X1 Carbonを手に入れ、環境を作っていました。これまではホストOSをWindowsに、開発などではゲストOSをLinuxにしていましたが、今回はホストをLinuxにしようとしていろいろ試してみました。結論としては、これまでと変わらずホストWindowsになってしまったのですが、それまでの経緯や調べたことなどを記録しておきます。誰かの参考になるかもしれないので、役に立ったリンクも張っておきます。

3行まとめ

  • ホストLinuxでは新しめのハードウェアに苦労する
  • GUIアプリの操作感は、ゲストOSはだいぶ不利
  • それ以外のパフォーマンスは、ゲストOSでも遜色ない

やったこと

新マシンのスペックはこんな感じです。

Windowsプレインストールですが、回復メディアを作ったらさっさと初期化、まずはUbuntu Desktop 14.04を入れてみました。そこから、Ubuntu上で暮らしていけるかの確認(主にハードウェア)と、Windowsをゲストにして作業が快適か(主にOfficeを使う)を、調べていきます。

ハードウェアについては、自分にとってはいきなり致命的だったのが、WiGigドックとBluetoothマウスの問題でした。WiGigドックはLinuxでの情報が見つからず(「数年以内にはサポートするんじゃない?」とか )、断念。

マウスはロジクールMX Anywhere 2Bluetooth接続ですが、Ubuntu 14.04ではPINなしのペアリングに対応してないとのこと。回答の中に「DON'T DO THIS!」とあったりもしたので、こちらも諦める方向(実のところPPA追加だけなので、大した騒ぎではないのですけど)。

また指紋認証も、fprintのサポート外デバイスらしく、使えるようにできませんでした。 (こちらのリンクの138a:0090に該当。)

Windowsゲストの導入

気を取り直して、Windows 10をゲストでインストールします。qemu+kvmVirtualBoxをそれぞれ試しました。Windows10はインストールUSBディスクを作っておいたものの、VMをUSBブートさせる方法がわからず、isoイメージから。基本的には問題なかったものの、引っかかった点をいくつか。

Windows上ではブラウザやIntelliJ IDEAを操作してみましたが、もっさり感があるのと、キーリピートが不自然に中断されるなどの微妙な使いにくさがありました。(これは対策したけど変わらず)

ベンチマーク(要約)

その後、新マシンにあらためてWindowsを(ホストとして)新規インストールして、さらにVirtualBoxHyper-VそれぞれでUbuntu Desktop 14.04を入れ、PCMark8や、pythonベンチマークを取ってみました。ここでは要約だけで、より詳しくは下の方に載せておきます。

PCMark8はWindows専用で、ブラウジングなど一般的なアプリケーション利用の操作感のスコアを出してくれる、ようです。ホストの時は2644、ゲストの時は1250前後となり、半分くらいでした。ただし項目ごとに見ると、差があるのは一部の項目だけで、そこまでの差はないようです。

Pythonでは総合的なベンチマークを取りました。こちらの結果は、傾向としてLinuxホスト>Linuxゲスト>Windowsホスト>Windowsゲストという、わかりやすい順位になりました。

ベンチマークのスコアを見る限りでは、どちらがホストでも極端な差は出ないようです。ただしアプリケーションの操作感は、Linuxホスト/Windowsゲストだとどうも使い辛く感じます。気のせい、プラセボ、思い込みかもしれません。

当面の結論

なにより、使いたいハードウェアを使えないという点で、WindowsをホストOSにして(もう少し)生きていくことにしました。ただし開発はゲストのUbuntu Desktop上で、IntelliJ IDEAも含めて使っていくつもりです(Windows上のPythonは、慣れたけど、やっぱりツライ)。

環境にかける情熱がだいぶ減っているなあというのも発見でした。なんとかなりそうでも、すぐ面倒くさくなる。いったん解決できても、今後のことやまたトラブル出たらと、思ってしまう。前にやった解決方法とかも、毎回忘れてるし。

そういえばゲストWindowsでOfficeを使ってみれば良かったなあ(面倒なので諦めた)。

ベンチマーク結果

PCMark8のベンチマークWindows専用です。無料で使えるHome Conventional 3.0を実行します。結果、ゲストではホストの約半分のスコアでした(2644に対し1250前後)。qemu(1259)とVirtualBox(1235)では、目立った差はありませんでした。

しかし項目別に見ると、Casual GamingとVideo Chat encodingが極端に悪いものの、それ以外ではそこまでの差はないように見えます。(いずれも、ホストの場合を1.0とした相対値。[s][ms]は時間なので長い方が遅く、[fps]は短い方が遅い)

ゲストを実際に操作すると、もっさりというか、引っかかるというか、使い辛い気がするんですが、ユーザーの操作に対する反応性だけであって、総合的なパフォーマンスは悪くないようです。

pythonのパフォーマンスはPython benchmark suiteというものを使いました。様々なベンチマーク(純粋なPythonの処理から、Web、DB、jsonxmlなどまで)をまとめて実行してくれるものです。画面表示・描画はありません。
グラフはそれぞれのかたまりがベンチマークの種類に対応し、その中の7本のバーが左から、以下の順序になっています。長さは実行時間で、長い方が遅くなります。いずれのベンチマークでも、Windowsゲストを1.0とした相対値になっています。

  1. Windowsゲスト(VirtualBox上)
  2. Windowsホスト
  3. Linuxゲスト(Hyper-V上)
  4. Linuxゲスト(VirtualBox上)
  5. Linuxホスト
  6. X240sでのWindowsホスト(参考値)
  7. X240sでのLinuxゲスト(Debian。参考値)



全体的な傾向としては、前述のように 5>4>3>2>1の順に速く(グラフは短く)なっています。項目によっては、Windowsのほうが速かったりと、異なる傾向のものもあります。(なお、極端な差が出たり、環境によって実行できなかった項目は、除いています。) Linuxゲストは、Hyper-VよりVirtualBoxのほうが成績が良いようですが、厳密に同じ設定になっていないかもしれないので、ここは参考程度に。

なおPython benchmark suiteで利用しているGitHub - vstinner/perf: Toolkit to run Python benchmarksというモジュールが現在Windowsで動かず、パッチを作って使いました。うまくいけばそのうち取り込まれると思います。

一歩ずつ進める

あなたが解きたい問題には、簡単なものもあれば難しいものもあります。
単純なものもあるし複雑なものもあります。とても簡単でとても単純な問題だったら、一発で解決できるかもしれません。しかしもう少し難しかったりもう少し複雑だったりすると、一発では解決できなくなってきます。こうした問題は少しずつ、一手ずつ解いていくことになります。

大きくて複雑な問題を一息で解決できれば、カッコいいかもしれません。
しかしそのやり方にはリスクがあります。一息でやっつけようとして、それが上手くいかないと、振り出しに戻ってしまい一歩も先へ進めません。ことによっては、最初よりも状況が悪化してしまうかもしれません。カッコよさよりも、問題解決という目的を大切にして、確実で効果のある進め方が必要です。小さな問題でも大きな問題でも、自分に合った歩幅で一歩ずつ進めましょう。ひとくちサイズずつ食べるという言い方もします。

一歩ずつ進めるアプローチにはメリットがたくさんあります。一歩で考えるのは小さな問題、元の問題から切り出した一部分になります。そうすると同時に考えないといけないことが減って、比較的簡単に解決できるようになります。問題はわかりやすく、解決方法もシンプルで、善し悪しも客観的に評価しやすくなります。解決方法が正しいかどうか、判断も容易です。考慮漏れや見落としが入り込む可能性も小さくなります。さらにより良い解決方法を求めて、方法自体を洗練させたり、他の案を検討したりもできます。

例題:
  • 皿洗い問題 汚れたお皿が10枚あります。洗って拭いて食器棚にしまわないといけません。
    • どういうやりかたをしますか?
    • 100枚だったらどうですか? 10000枚だったら?

自分の歩幅

一歩ずつ進めるといっても、どんなふうに一歩を決めればいいでしょうか。Webアプリの開発を例にすると、たとえば次のように6つに区切るという手があります(あくまで一例です)。

  1. 画面を実装して入力できるようにする
  2. 入力したデータをどう保持するか設計する
  3. 入力と既存のデータを組み合わせて保存する実装をする
  4. ユーザーが使いやすいよう画面を修正する
  5. 全体が矛盾なく整合しているかテストする
  6. 実行時間が遅くなったりしないか確認する

1ステップごとに、プログラムが少しずつ増え、完成に近づいていきます。2番は設計を検討するだけですし、5番や6番はテストやレビューが中心で、コードは書かないようなステップもあります。

こうした「一歩の大きさ」は様々です。ベテランならば1番から6番までまとめて一歩で片付けてしまうかもしれません。新人だったら、もっと細かく分けないとうまく進めないかもしれません。あるいはベテランでも、1画面に300項目あるような複雑な機能なら、念入りに小さな一歩で進めるでしょう。Rubyならまとめてできる人でも、C++では設計だけ先に考えるということもあります。問題の性質、人の経験やスキル、解決に使う道具やテクノロジーによって、妥当な一歩の大きさが変わってきます。自分が自信を持てる、これなら踏み外したり、うっかり間違えたりしないと思えるような一歩を、そのときどきで選んでください。

プログラミングの世界ではよく、設計・実装・テストという作業をしますが、こうした「作業の分解」も一歩ずつ進める一つの方法です。

  • 設計: 対象の問題全体を解決する方法を決める
  • 実装: 設計で決めた方法を具体化する
  • テスト: 問題が解決したか確かめる
例題:
  • 普通の一歩問題 普通に歩いているときの、一歩の大きさを測ってください。そのうえで、「自分が自信を持てる」一歩の大きさは、どのくらいだと思いますか?
    • 平坦な道路では?急な山道だったら?ツルツルに凍った氷の上なら?
    • 裸足のとき、履き慣れた靴のとき、ビーチサンダル、下駄、竹馬ではどうでしょう?
    • 空中100メートルで綱渡りをするとしたら、一歩の大きさはどのくらいですか?一歩でも進めますか?
  • 間違えない問題 プログラムを書くとき、何文字、あるいは何行までなら絶対に一箇所も間違えないで書けますか?
    • 確かめてください。
    • 自分で自信があると感じる度合いと、実際に間違えてしまう割合は、合っていますか?

前に進まない一歩

一歩ずつ着実に確実に前に進むという考え方ではあるのですが、前に進まない一歩というものもあります。進めていくうちに、わからないところが見つかることがあります。最初の想定と違う箇所が出てくることもあるし、今までやったことが間違っていたとわかることもあります。単に、自信が持てなくなることだってあります。そうしたときは、前進するのをいったんやめて、考え直さないといけません。状況を再確認したり、方針を見直したり、情報を収集したりするのも、前に進まない感じがありますがこれもまた一歩です。

大きな問題を少しずつ解決するのは、長い道のりを一歩ずつ進むようなものです。一歩ずつ進んでいると、先行きがわからなくなったり、見失ったりしやすくなります。全体として問題解決に近づいているかどうか、ときどき周りを見渡し、行く手を確認したほうがよいでしょう。本当に前進しているか、解決に近づいているか、事実の確認と仮説の検証をしながら進んでください。道を間違えていると困るので、ときどき行く手を探索して情報収集しましょう。むしろ、一歩ずつ進んでいるからこそ、問題点や不明点に気づくことができます。

コラム 前に進まない楽しい仕事

プログラミングというのは楽しいものです。 楽しいことをしていると、つい時間を忘れて没頭しがちです。 やっていることが面白くなって、もともとの目的を忘れてしまうことも、またありがちです。 コードの美しさにこだわったり、アルゴリズムのチューニングに熱中したり、モデリング中毒になったり、アニメーションの気持ちよさを追求したりと、ハマりどころはたくさんあります。

そういう点ももちろん大事なので、かけるべき時間はかけましょう。しかし今やらないといけないのか、どれくらい時間をかけるべきなのか、意識して確認しましょう。先にやるべきことは先にやる、後に回せることは後にするのが大事です。

いま、なんのために、どんな一歩を踏んでいるところなのかを忘れずに。

解としての一歩ずつ

そもそもプログラミングとは、問題を解くために、コードを1行ずつ書くことです。プログラムはまさしく、一歩ずつの積み重ねでできているわけです。アルゴリズムを勉強していると、解説はたいてい、1ステップずつの処理に分けて書いてあります。分割統治との組み合わせで、様々なアルゴリズムにおいて、扱いやすい一部分だけを取り出して処理するというアプローチが利用されています。問題解決の方法と進め方はどちらも、一歩ずつ進めるのと相性がいいのです。

一歩ずつのアプローチは、後から他の人が見たときも理解しやすくなります。理解するほうも、やはり一歩ずつ理解を進められるためです。一歩ずつの考え方を反映したプログラムやシステムの構造も、同様です。

例題
  • 問題分解問題 スマホアプリに入力したデータをサーバで集計して、運用者がGoogle Spreadsheetで見られるようにしたいとします。
    • まずこの問題を3つの問題に分解してください。
    • 3つに分けたそれぞれを、さらに小さな問題に分解してみてください。ここでも一歩ずつ進めましょう。一気にうんと小さくしようとせず、2つか3つくらいずつ、段階を追って分解してください。

https://github.com/yattom/text_for_programmer/wiki/%E4%B8%80%E6%AD%A9%E3%81%9A%E3%81%A4%E9%80%B2%E3%82%81%E3%82%8B

抽象化

問題を解決するには、まず問題を把握しなくてはなりません。 しかし一歩ずつ進めるうえでの最初の一歩から悩むこともあります。 たくさんの要素を同時に考えないといけなかったり、多くの事柄が複雑に関連していて、小さな部分に切り出すのが難しいことがあります。 分割統治するための分割が、難しいわけです。 いちどに把握して検討できるものの数には限度があって、それ以上になるとまとめて考えることができなくなり、見落としや考慮漏れが発生します。

例として、週末にパーティーを開くために買い物をすることになったと考えてみましょう。 ほしいものを考えながら買い物リストを作っていたら、ものすごく長くなってしまいました。 全部で100個はありそうです。 近所のスーパーで買えるものもあれば、駅前の商店街で買いたいもの、デパートまで行かないといけないものもあります。 これを整理しないで買い物に出かけたら、スーパーと商店街を何回も往復したり、せっかくデパートまで行っても買い忘れをしたりしそうです。

そこで、買い物リストを分割することにします。 どこで買うかによって、リストを3つに分けます。 これで、スーパーの買い物リスト、商店街の買い物リスト、デパートの買い物リストの3つのリストができました。 これで買い物を無駄なく、忘れ物もなくできそうです。

いまの例では、100個以上の項目がある「買い物リスト」を、「どこで買うか」という観点で整理して3つの買い物リストにしました。 整理する前の問題は「リストを見ながらどこで買うか考えながら買い物をして回る」というもので、移動と買い物を同時に考える必要がありました。 100個以上も頭の中だけで整理していたら、移動の無駄が起きたり、買い忘れが起きてしまいます。 整理して3つの買い物リストに分けたおかげで、今度は問題も分割できました。 まず「買い物する場所に行く」という問題、そして「その場所で買い物をする」という問題の2つです。

「買い物リスト」を「どこで買うか」という観点で見直す、これが抽象化の一例です。 大きく複雑なひとかたまりの問題を、観点によって整理し、簡単に解けるいくつかの問題に分割するのです。 細かい点を見ないようにしたり、差異を無視して同じものと扱ったり、似た点があるものをグループ化したりするのが、抽象化です。

「抽象的」は「具体的」の反対の意味です。 抽象化とは、具体的の反対にすることなので、必ず情報が失われます。 このことから「抽象」は「捨象」とも言います。 情報を捨てるという意味です。 いま着目したい、重要視したいところだけを取り出し、それ以外の情報は無視して考える。 それが抽象化です。

例題:
  • 最寄り駅から家までの地図を書いてください。書き終わったら地図を観察して、どんな情報が載っているか、どんな情報は捨てたのか書き出してください。なぜその情報を選んだのか、考えてみてください。
  • 会社で泊まりがけの合宿をすることになり、宿泊場所の候補を探してほしいと頼まれてしまいました。いくつかの宿泊施設を選んで、それぞれの特徴や情報を整理して一覧表にしないといけません。どういう情報を載せますか?
    • 家族や仲間との遊びの旅行だったら、載せる情報は変わりますか?それはなぜですか?
  • ブラウザで適当なWebページを開いてください。そのページの構成や内容について、抽象化して説明してください。

抽象化のレベル

整理した3つのリストを、3枚のメモ用紙に分けて書いて、机の上に置いてみましょう。 ここからちょっと離れてみてください。 2メートルくらい離れると(すごく目のいい人なら5メートルくらい)、もうリストになにが書いてあるかはわからなくなるはずです。 それでもリストが3つあることはわかります。 左がスーパー、真ん中が商店街、右がデパートで買うもののリストです。 これくらい離れると、買うもの個々のことは気にせず、どこに買い物に行くかだけ考えられます。 デパート→商店街→スーパーの順に回るか? 商店街とスーパーは仕事帰りに寄って、デパートは土曜日にするか?

行く場所が決まったら、また机のところに戻りましょう。 そしてこれから最初に行く場所のメモを1枚取って、目の前に持ってきてください。 何を買うのかよく見えるようになったので、買い物の内容をチェックできます。 飲み物は3本で足りるか?料理の材料に忘れ物はないか?花を飾ろうか、いらないだろうか? このとき、他のリストは見えなくなっています。 一箇所の買い物だけに集中できて、その後どこへ行くのか気にしなくてすみます。

物事を抽象化すると、それぞれ異なる抽象化のレベルが見つかります。 この例では「リストを選ぶレベル」と「買い物で使うレベル」の2つできました。 それぞれのレベルでは、それぞれの問題に集中できます。 そしてあるレベルで考えたり判断したことは、他のレベルに影響しません。 いまは「スーパー」「商店街」「デパート」の3つに整理していますが、たとえば商店街のどの店で買うのか、デパートのどのフロアに行くのかを考えたくなるかもしれません そうすればレベルが増えて、商店街を回る順番や、デパートで寄るフロアを考えることもできるようになります。

具体的に近い方を「低レベル」、抽象度の高い方を「高レベル」と呼ぶこともあります。 これは誰かをほめたり、けなしたりしているわけではなく、単に抽象化の度合いを表している言葉です。 高い低いを、空を飛ぶ鳥や飛行機のような高度でイメージしてみてください。 地面に近い、低いところでは、いろいろなものが詳細に、具体的に見えます。 地面から遠く、高い高度まで上がっていくと、広い範囲が見渡せるようになります。 低レベルでは詳細度が上がりますが視野が狭まり、高レベルでは詳細は見えませんが視野が広くなります。 どちらが大事とか、偉いとかいう話ではありません。 それぞれのレベルで見えるものが違い、考えられることも、解決できることも違うという考え方です。

問題のどの部分を、どのレベルから解決していくのか意識すると、問題全体にどうアプローチしていくのか作戦が立てられるようになります。 そうすると抽象レベルを高く上がったり低く下がったり、行ったり来たりしながら問題に取り組むことになります。 自分がいまどのレベルで考えているのか、そこでどういう問題を解こうとしているのか、忘れないようにしましょう。

例題:
  • Googleマップで自宅周辺の地図を表示して、拡大・縮小してみましょう。4つくらいのレベルを決めて、それぞれ比較してください。(スクリーンショットを保存して見比べるとよいです。)
    • それぞれのレベルでは、何が見え、何が見えないですか?どんなことがわかりますか?
    • それぞれのレベルの地図は、どんな使い道に向くと思いますか?
    • 同じことを、衛星写真(Earth)でやったり、交通状況を切り替えたりして、比較してください。
  • ふだん使っているソフトウェアやアプリについて、4つの抽象レベルで、それぞれ100文字程度で説明してください。
    1. 今日(または最後に使ったとき)の具体的なやったこと、使い方
    2. 自分がなんのためにそのソフトウェアを使うのか、その目的
    3. 類似するソフトウェアと比較した特徴
    4. 90歳のおばあちゃん(または3歳の子供)にそのソフトウェアを説明する
    • それぞれの説明の違いはなにか、なぜそのように変えたのか、整理してください
    • 他にどんな抽象レベルがあるか、考えてみてください
      • 例: そのソフトウェアの開発者なら?その会社の社長なら?株式アナリストなら?ライバルなら?

観点が重要

ここまでの例や例題で見てきたように、抽象化はソフトウェア開発に限らず、あらゆる場面で使われています。 もちろんソフトウェア開発やシステム開発でも、問題解決のためさまざまな抽象化がおこなわれています。 システムは一般的に複雑なものなので、抽象化も様々な観点が必要となります。 設計書、仕様書などのドキュメントはほとんどが、なんらかの観点からシステムを抽象化して記述したものです。 (なおドキュメントの呼称や内容は、会社や現場、人によって大きく違うので、知っているのと違うと思うかもしれません。 あくまで一例です。)

  • ワイヤーフレーム
    • 観点は画面の種類や数と、表示する主要な情報、遷移の流れなど
  • フローチャート
    • 一連の処理がどこから始まりどこで終わるか、どう流れていくかという観点
  • 詳細仕様書
    • 対象としている範囲に限った、プログラミングに必要な情報をすべて含む観点
  • システム概要
    • システムに登場するもの最大粒度で表現する観点で、システムの境界を定め、対象と対象外を区別するのに使う

このように観点しだいで抽象化の結果も目的も変わってきます。 どういう観点を選べばいいのか、どういう問題にはどういう観点を使えばいいのか、これは知識と経験が必要になってきます。 また観点の選び方が悪いと、問題解決の役に立たないどころか、かえって事態を混乱させてしまうこともあります。

買い物リストの話で考えてみましょう。 どこで買うかという観点でリストを整理し、3つのリストを作って、「どこに行くか」と「その場所で何を買うか」を分離して考えられるようにしました。 ところで、もし花を買うとしたら、どこで買うといいでしょうか? 花はデパートでも買えるし、商店街にも花屋がありそうですし、スーパーでも売っています。 花を実際に見て値段も確認してから買おうと思ったら、どのリストに入れておくといいでしょうか。 もし3箇所見比べたかったら、「どこに行くか」を考えるとき考慮しないといけなくなります。 こうなると、せっかくリストを整理したのに、「花をどこで買うか」という新たな問題が発生してしまいます。

また別の観点もあるかもしれません。 たとえば、予算が決まっていて、ほしいものは全部買えないかもしれません。 リストが1つだけだったら、リストの中身を必要な順に並べ替えて、予算がなくなるまで上から順に買えばいいでしょう。 しかしリストが3つに分かれていると、どのリストをどこまで買うべきなのかわからなくなってしまいます。 全体の予算があると、「どこで買うか」という観点の抽象化は不適切なわけです。 先に予算の問題を解決しないと、適用できない観点だということになります。

観点と抽象化レベルによって、もともとの大きな問題を分割してそれぞれ個別に解いていけます。 しかし、分離しきれない問題が残ることもあって、これはレベルをまたいで考える必要があります。 観点が間違っていて、問題が複雑化してしまうこともあります。 もともとの問題を全部理解して把握できていれば、あとからこんなことに気づいたりしないですむかもしれません。 しかしそもそも、問題は複雑で理解も把握も難しいから、いろいろ工夫をしているわけです。 「考えればわかること」は「考えたからわかる」ので、最初からわかっていれば苦労しません。

そこで、抽象化がうまく働いているか、問題がきれいに分割できているか、進めながら確かめる必要が出てきます。 高レベルで問題を解決したら、それが低レベルでもちゃんと解決できているか調べます。 また、他のレベルに不要な影響を与えていないかも、要確認です。 こうした確認を通じて、設定した観点と抽象レベルが妥当なものだったかわかってくるのです。 抽象レベルを自由自在に行き来しながら問題を解決し検証するというのは、とても高度なスキルですが、これができるようになるとプログラマとしての問題解決能力が大きく広がります。

コラム: アーキテクトも実装する

ソフトウェア開発ではよく「上流の設計が、下流で矛盾を生む」という事態が起きます。 その理由のひとつが、こうした抽象化の誤りです。(理由は他にもたくさんありますが。) 上流SEや、アーキテクト、プロジェクトリーダーといった立場の人が、高レベルのことだけ考えて低レベルでの確認を怠ると、実装時に設計を考え直さないといけなくなったり、実装できない設計になったりしてしまいます。

『組織パターン』には「アーキテクトも実装する」というパターンがあり、全体の方向性を決定するアーキテクトもプログラマーと一緒にコードを書くことの重要性を説いています。 また『人月の神話』の「第3章 外科手術チーム」では、生産性の非常に高い10名の開発チームには「執刀医」に当たるチーフプログラマーがいて、「自ら機能と性能を定義し、プログラムをデザインし、コーディングし、テストして文書を書く」と述べています。 ソフトウェア開発において実装というのはかなり具体的な活動、低レベルなものですが、ここまで下りてこないと本当には確認できない問題というのがたくさんあるのです。 (ちなみに、さらに低く、OSやメモリ、通信プロトコルや物理マシンといったレベルまで下がっていくこともできます。)

例題:
  • 何人かのグループを男女に分けるという場面をよく目にすると思います。どんな状況があるか、男女に分けることでどんな問題を解決しようとしているのか、いくつか書き出してください。
    • 男女に分けるという問題解決が、どういった観点からもたらされたのか、考えてください。
    • 男女に分けたせいで問題が起きることもあります。どんな観点だと、かえって状況が悪化すると思いますか。
  • いま利用しているプログラミング言語を、他の言語と比較しながら、なぜ自分がいまそちらを使っているのか説明してください。できれば他の言語は2つ以上選んでください。

https://github.com/yattom/text_for_programmer/wiki/%E6%8A%BD%E8%B1%A1%E5%8C%96