WEB+DB PRESS Vol.112
読んだ感想とまとめ
縁の下のUIデザイン カードUIの向き不向き
- スマホUIは現実のものを模したデザイン(スキューモフィズム)からフラットに
- カードUIはなんとなくとっつきやすく使いがち
- カードUIは立体感や影をつけて使うことから、相対的に要素に存在感を出すことができる
- 強弱をつけたいデザインに効果的
- 不均一な情報をきれいに整理できる
- 例えば高さが違ったり
- デザインに必要な要素が多いのでごちゃつかないように意識する
- カードによる入れ子は余白が多くなるのでやめたほうが良い
- ツイッターのような短文の投稿の場合1つ1つの重みより全体のみやすさが重要なので、カードUIは適さない
- レビューのようなしっかりしたものはカードUIで
- PCではテーブルが多いがそれをカードUIにするときは気をつける
- スマホでテーブルを使うと横にスクロールする必要があり使いづらい
- 逆にPCでカードを使うとテーブルと比べて見づらい
- カードUIと違うUIを作り比べることで使う理由をきちんと明確化する
at the front 今生きるプログラマーが、この仕事をあこがれのものにする
- 「高度なコンピュータ・サイエンス、プログラミングスキルの現場適用の難しさ」が課題だった
- フレームワークの中にある低層の基礎の必要性
- 木構造の差分適用というアルゴリズムがUIの更新を抽象し、パフォーマンスと開発効率を両立させる
- 高度な技術もそれを必要とする人に説明しなければ、技術で現実的に信頼できる品質で実装できなければ、有用であると言えない
- 適切な場所とタイミングで提示するためには、求められているさまざまな期待を理解し超える必要がある
- プログラマーが憧れの対象であるために社会の期待に答え、本質を追い求める必要がある
- FWが死んでも思想は継承されるはず
- 流行り廃りは必ず発生して、変化を受け入れる覚悟必要である
Special Report ScalaMatsuri 2019
- 2019/6/27 - 29
- 初日はワークショップ形式、2日目は公園形式、最終はアンカファレンス
Special Report elm Europe 2019
- 2019/6/27 - 28
- Elmによるゲーム開発、JSのパフォーマンスチューニング、スケール
モダンフロントエンドの構造化と分割の新提案 コンポーネント設計
- メンタルモデルを活用した設計、実装
- 関心の分離により疎結合を実現
なぜコンポーネント設計か
- アプリ開発は下記のようなフローで動く
- ニーズ分析/企画
- 要求分析/要件定義
- 仕様策定
- UIデザイン/プロトタイピング
- 設計/実装
- プロトタイピングツールの充実のおかげで、共有やFBしやすくなっている
- しかしデザインツールだけでは齟齬が発生することが度々ある
- デザインが完成してから開発をする場合、互いに関与できることは少ない
- 画面単位で考えているので待ちが長くなる
- 小さくすることで解決を図る(コンポーネント単位で考える
ビューコンポーネントの現在
- ビューコンポーネントは外部からデータを受け取り画面に表示をおこなう
- また、コンポーネント内のみ扱えるデータをもつ(ボタン非活性フラグ
- 複数コンポーネントを子コンポーネントでまとめられる
- 基本的に子コンポーネント同士でデータは受け渡しをせず親子で行う
- Reactではデータフローは一方向
import * as React from "react" import { Switcher, OptionLabel } from "./components" interface Props { heading ? : string; } interface States { display: boolean; } // クラスコンポーネント export class DisplaySwitchClass extends React.Component < Props, States > { constructor(props: Props) { super(props) this.state = { display: false } } render() { const toggleEvent = this.onChanged.bind(this); const display = this.state.display; return ( < > <StyledHeading> {this.props.heading} </StyledHeading> < OptionLabel text = "表示" onClicked = { toggleEvent } /> < Switcher toggle = { display } onChanged = { toggleEvent } /> < / > ) } onChanged() { this.setState({ display: !this.state.display }) } } // 関数コンポーネント + React Hooks import { useState } from "react" export const DisplaySwitchFunc = (props: Props) => { const [states, onChangeDisplay] = useState < States > ({ display: false }); const { display } = states; const toggleEvent = () => { onChangeDisplay({ display: !display }) } return ( < > <StyledHeading> {props.heading} </StyledHeading> < OptionLabel text = "表示" onClicked = { toggleEvent } /> < Switcher toggle = { display } onChanged = { toggleEvent } /> < / > ) } const StyledHeading = styled.h1 ` font-size: 16px; `;
- Vue.jsはデータフローが双方向、DOMに対してディレクティブという値を代入したり、イベントを設定できる
- 子から親へデータを渡せたりする
- stateは存在せず、dataオブジェクトがある
- 子から親へデータを渡せたりする
<template> <div> <StyledHeading> {{heading}} </StyledHeading> <option-label text="表示" :onClicked="onChanged" /> <switcher :toggle="display" :onChanged="onChanged" /> </div> </template> <script> import styled from "vue-styled-components"; import { OptionLabel, Switcher } from "./components/"; const StyledHeading = styled.h1 ` font-size: 16px; `; export default { name: 'display-switch', data: function() { return { display: false } }, props: { heading: String }, components: { OptionLabel, Switcher, StyledHeading }, methods: { onChanged() { this.display = !this.display } } } </script>
- ビューコンポーネントにおける再利用性
- どこまで分割を行うべきか
- デザイナーは下記2点を重要と考える
- ビジュアル(=見た目)や操作性に一貫性を持たせ、ブランドを守る
- デザイナーどうしの協業を推し進め、効率化を図る
- Atomic Designという考え方がある
- 粒度ベースで分割を行うと下記メリットが生まれる
- 開発メンバー間で意思疎通しやすくなる
- ビューコンポーネントとしてコードを小さくできるため、変更に強くなる
- 誰でも部品を組み合わせるだけで画面をある程度作成できる
- デメリット
- 最近は粒度で分割しないほうがいいのではと考えられ始めた
設計の前に考えること/行うこと
- アプリケーションの中でビューコンポーネントはどのような性質や役割を持つべきか考える
- アプリケーションがどういうものなのかを理解しておく
- 人間が理解するという活動は「学習」と「イメージ化」という大きな流れに分解できる
- この理解の流れで作られるイメージを「メンタルモデル」と呼ぶ
- 人や環境によってメンタルモデルがずれることがある
- トンカチを「釘を叩くもの」「ガラスを割るもの」とずれる
- 叩くは同じだが、何を叩くがずれている
- アプリケーションも同じようにずれることがよくある
- 文脈によっても同じメンタルモデルの話をしていてもずれる
- トンカチの柄だけを見ると、木の棒と認識してしまう
- サービスや業務の領域に関係するメンタルモデルからアプリケーションを理 解する
- 「サービスや業務の領域」のことを「ドメイン」と呼ぶ
- これは開発者だけでなく、使うユーザーにも言える
- メンタルモデルの抽出はユーザーの活動に注目し、「名詞と動詞の簡単な文章」にする
- 宛先管理サービスを例にすると
- ユーザーは、宛先をサービスに登録する
- ユーザーは、宛先の名前を記入する
- ユーザーは、宛先住所の郵便番号を記入する
- ユーザーは、宛先住所の都道府県、市区町村、丁目と番地を記入する
- ユーザーは、サービスに登録している宛先を変更する
- ユーザーは、サービスに登録している宛先を削除する
- ユーザーは、サービスに登録している宛先を参照する
- 名刺を抽出する
- 宛先、名前、郵便番号、住所、都道府県、、、
- その後名刺の定義を行う
- 例 宛先:宛先とは何かを届けるための情報。届ける先の人の名前と郵便番号と住所が必要
- 類義語はどちらの名詞を主とするか定義しておく
ビューコンポーネントの設計と実装
- ドメインのコンポーネントを作っていく
- 抽出した名詞をもとにドメインコンポーネントを作る
- グループ化できるものを探す
- 住所は都道府県、市区町村などを持つ
- 宛先は名前、郵便番号、住所などを持つ
- そのままコンポーネントに定義する
const Destination = (props: Props) => { const { name, postalCode, address } = props const postalCodeStrings = ` ${postalCode.upper} - ${postalCode.lower}` const addressStrings = ` ${address.prefecture} ${address.city}${address.street}` return ( <> <span>名前</span> <strong> { name } </strong> <span > 郵便番号 < /span> <strong > { postalCodeStrings } < /strong> <span > 住所 < /span> <strong > { addressStrings } < /strong> </> ) }
const Name = (props: Props) => { const name = props.value return ( <> <span>名前</span> <strong > { name } < /strong> </> ) }
<Name value={name} /> <PostalCode upper={postalCode.upper} lower={postalCode.lower} /> <Address city={address.city} street={address.street} />
- これらのコンポーネントはGUIを通して表示される
- ユーザーはwebのなどのGUIの部品にイメージを持っている
- また、画面というドメインモデルが存在する
- ドメイン/GUI/画面などのメンタルモデルを1つのコンポーネントの中で考えるべきではない
- 関心事によって分離する
- オフィスの受付にある受付アプリは「受け付け担当者を呼ぶこと」と「ボタンをタップしたら処理を開始すること」という2つの役割が存在する
- ドメインの関心は「受付担当者を呼ぶこと」
- GUIの関心は「ボタンはタップできること」
- 関心事とは、対象のモノ/コトに対して持つ役割や興味の範囲を指し
- 同じ関心事にあたるメンタルモデルやその振る舞いを1つにまとめて整理することが関心の分離
- ドメインオブジェクトは要素を持つ関心事
- GUIは表示の関心事を持つ
- アフォーダンスという環境から得られる情報を鍵にして最適化を行う
- 画面は遷移、何を表示するかに関心を持つ
- レイアウトというドメイン同士の間隔に関心を持つ
- 状態やコンテナ
- ドメインオブジェクトは、ドメインエレメントを要素として持つ
- ドメインエレメントは、GUIパーツで表示/入力を表現する
- GUI パーツは、組み合わせて使うことがある
- 画面は、面として表示し遷移をする
- レイアウトは、配置に関して調整する
- コンテナは、アプリケーションの状態データの流し込みを行う
- 画面での入力はコンテナを経由しドメインオブジェクトに流れ、ドメインエレメントを変更し、GUIが表示を反映する
開発フロー
メリット
- 並列的に、ビューコンポーネントの設計/デザイン/実装を小さく行えるようになる
- デザイナー/エンジニア間でアプリケーションを一緒に理解できるようになる
- デザイナー/エンジニア間で待ち時間などを少なくできるようになる
画面定義
- ユーザーが使いやすいアプリケーションはタスクベース(動詞的な画面のグルーピング)ではない
- オブジェクトベース(名詞的な画面のグルーピング)によるナビゲーション
これをOOUI(オブジェクト指向UI)と呼ぶ
ブログをタスクベースにすると
- 編集する
- 記事
- プロフィール
- テンプレート
- 追加する
- 記事
- テンプレート
となる
オブジェクトベースの画面遷移は
- 記事(一覧)
- 詳細
- 編集
- 追加
- プロフィール
- 変更
- テンプレート(一覧)
- 詳細
- 編集
- 追加
となり、わかりやすい
実践チュートリアル
- アパレルECサービスを例に設計をする
要求、要件定義
- 商品を閲覧して購入できる
- 商品はカテゴリ別やブランド別に分類して閲覧できる
- 購入したい商品をカートに入れてまとめて購入できる
- 購入したい商品のサイズやカラーなどを選ぶことができる
- 購入すると商品を配送会社を使って配送できる
- 配送にはいくつかの宛先を登録でき、その中から選ぶことができる
- 支払いには現金/クレジットカードとペイサービスを利用できる
画面遷移図やワイヤーフレームを書くのではなく、ユーザーやサービス提供者の活動を、名詞と 動詞の簡単な文章に表す
- ユーザーは、商品をカテゴリから探す
- ユーザーは、商品の一覧を参照する
- ユーザーは、商品の一覧から商品の写真やスペックなど詳細情報を参照する
- ユーザーは、商品詳細でバリエーション(サイズやカラー)を選ぶ
- ユーザーは、商品をカートに入れる
- ユーザーは、カートに入れた商品を確認する
- ユーザーは、購入を確定する
- ユーザーは、お届け先を記入する
- ユーザーは、お届け先を追加する
- ユーザーは、カートの商品に関して支払いを行う
- ユーザーは、決済完了したか確認をする
- ユーザーは、発送/配送状況を確認する
世に存在しないアプリの場合は、似たサービスをもとに考える
その後名詞の定義を行い、オブジェクトの抽出を行う
- 商品
- 商品名
- 写真
- カテゴリ
メンタルモデルが数多くあり、複雑なときは、メンバーによるワークショップ形式ですり合わせを行う
抽出後はOOUIで画面の定義を行う
タスクベースにすると探すから商品、カテゴリ、ブランドと分岐する
ユーザが何をしたいかというと
- A:決まっている商品を探す
- B:商品カテゴリを見る
- C:ブランドを見る
となるので、探すは興味のある商品に紐づく
商品からカテゴリやブランドに紐づく
検索から商品、カテゴリに紐づく
メンタルモデルから得られるもの
共通言語
建築ではパターン・ランゲージと呼ばれる
DDDではユビキタス言語
正しさの基準
- メンタルモデルを理解することで、何がアプリケ ーションにとって正しいか判断しやすくなる
- メンタルモデルは変わっていくものだが、基準を 一度作れば変化を捉えやすくなる
- ユーザーが持っているメンタルモデルと同じものを 持つため、ユーザーを理解することにもつながる
RDBMS徹底比較
- PostgreSQL、MySQL、SQL Server、Oracle Databaseを比較
- OS から見たアーキテクチャ、SQL、オブジェクトの 3 つの視点で比較
アーキテクチャを比べる
Oracle Database
- 大規模システムのRDBMSとして大きなシェアを持っている
- 高速なレスポンスが要求されるOLTP(OnLineTransactionProcessing)に対応できる
- 高スループットが要求される分析システムに対しても対応できる
- DB変更処理をオンライン状態のママ実行できる
- 障害対応のためのクラスタウェアやファイルシステムも提供する
- ライセンスなど高い
- RealApplicationClusters(RAC)はスケールアウトによる拡張で独自機能
- ストレージを共有する複数のホストで構成され、ホストの追加により仮想的に利用できるプロセッサ数とメモリ量を拡大する
PostgreSQL
- 比較的小規模なサイズに使われる
- 1970年代に開発されたIngresを継承し、1989年に開発されたPostgresを直接の子孫とする
- OSS
- Amazon Redshift、Amazon Aurora、 Greenplum Database、EDB Postgres Advanced Serverなどに派生する
- 拡張性が高く、自由度が高い
MySQL
- webのバックエンドなど比較的シンプルなSQLを高速に動作させる環境に適しているオープンソース
- Amazon Aurora や Percona Server な ど の 互換 RDBMS や、 MariaDBのような派生製品が開発されている
- OSに対する負荷が小さく、レプリケーション機能が搭載されている
- 複数のストレージエンジンを利用できる
- 他と比べると機能が少ないが8.0.2あたりから機能が増えて他と大差ない感じになってきた
SQL Server
- Microsoft Windowsと親和性が高く、Azureの基盤となっている
- Microsoftが作っている
- Oracleと並んで高機能、Linuxでも動く
- 高度な自動化が特上。ELT機能、レポート機能、分析システムが同時にインストールされる
プロセス構成
- RDBMSが実行されている状態をインスタンスと呼ぶ
- プロセス
- メモリ
- ストレージ
- インスタンスのプロセス構成は、マルチプロセスモデルとマルチスレッドモデル
- マルチプロセスモデルはユーザーからの接続ごとに新しいプロセスを起動する
- 特定のプロセスに障害が発生した際の影響範囲を小さくできる利点がある
- Oracle,Postgreが採用している
- マルチスレッドモデルはプロセス内に複数のスレッドが作成される
Oracle
- インスタンスは複数のプロセスから構成される
- インスタンスとは別にクライアントからの接続を受け付けるリスナーもいる
- インスタンスを維持するための必須プロセス(バックグラウンドプロセス)
- 接続ごとに起動されるプロセス(サーバープロセス)
- 両方独立したプロセス
- バックグラウンドプロセスは相互監視をしており、停止するとインスタンス全体が停止する
- サーバープロセスはSQLの解析、実行計画の作成、実行、結果の送信をする
- サーバープロセスに障害が起きても、トランザクションが自動的にロールバックされ他には無害
- クライアントの接続とサーバープロセスが1対1になる構成を「専用サーバー構成」と呼ぶ
- ネットワーク経由でクライアントからの接続を受け取るプロセスをリスナーと呼ぶ
- 通常ホストに1個
Postgre
- インスタンス全体を管理するpostmasterプロセスと子プロセスで構成される
- 子プロセスには必須のバックグラウンドプロセスが稼働する
- postmasterプロセスが接続を受けると、子プロセス(バックグラウンドプロセス)を作る
- マルチスレッドモデルは提供されていない
- 接続と切断が頻繁に行われるwebアプリなどでは、アプリケーションサーバーのコネクションプーリングを使って接続時のオーバーヘッドを軽減する対策をすることが一般的
- バックグラウンドプロセスが異常終了すると全セッションが強制的に破棄される(影響範囲を小さくするわけではない
MySQL
- インスタンスは単一プロセスとして動作する
- 内部的には複数スレッドが動作する
SQLServer
- インスタンスは単一プロセスとして動作する
メモリ構成
- 一度使用したオブジェクトをメモリ上にキャッシュとして保持して再利用する
- そのため大規模なメモリ領域が必要
- 全セッションで共有するメモリ領域と固有に持つメモリ領域に分割されている
- Oracle,SQL Serverは何でもキャッシュするが、MYSQLは実行結果や実行計画はキャッシュしない
- ソート処理などはセッションを管理するプロセスのローカルメモリに保存
- マルチプロセスモデルはOSの共有メモリ機能を使う
Oracle
- 全プロセスで共有するSystemGlobalArea(SGA)と接続単位に使用するローカルメモリProgramGlobalArea(PGA)がある
- SGAは共有プール、ログバッファ、バッファキャッシュなどがある
- SGA全体のサイズのみを指定して、内部の領域は自動的に行うことをおすすめする
- バッファキャッシュはテーブル、インデックスなどのデータをキャッシュする
- 大きな領域を確保してある
- 共有プールはオブジェクトの構成をキャッシュするディクショナリキャッシュ、SQL分の実行結果を保存するリザルトキャッシュなどから構成される
- ログバッファはトランザクションの情報を一時的んキャッシュする
- PGAはSQL分を実行するために確保するローカルメモリ領域
- 必要なときに必要なだけ確保される
Postgre
- 共有バッファは構成情報、テーブルのデータなどが格納される
- WAlバッファはトランザクションの更新情報を格納する(WriteAheadLog)
- ソート処理などはバックエンドプロセスのローカルメモリで行う
- メモリが足りない場合は一時ファイルを作るので、パフォーマンスが悪化する
MySQL
- 単一のプロセスなので共有メモリは使わない
- 全体で管理するメモリをグローバルバッファ、各セッションが利用するメモリ領域がスレッドバッファ
- グローバルバッファはバッファプール、ログバッファに分類される
- バッファプールはテーブルの情報をキャッシュする
- トランザクション情報を一時的に保存するログバッファ
- ソート処理などはスレッドバッファを利用する。メモリが足りないとストレージ上で実行される
SQLServer
- ローカルメモリのみ
- 構成は最大と最小のメモリを定義するだけ
ストレージ構成
- 永続化とパフォーマンス維持のため、トランザクションログとデータ両方に書きこみをする
- テーブルやインデックス内のデ ータを保存する領域と、更新トランザクションによる差分データ(トランザクションログ)を保存するストレージ 領域を持つ
- 更新が確定(コミット)するとトランザクションログとメモリの情報を同期的に更新する
- データ領域は古いままだが、チェックポイントという機能によりメモリとデータ領域が同期される
- トランザクションログは障害のときに最新にするために使われる
Oracle
- ストレージ領域はファイルシステムを使うこともできるが、最近ではストレージ管理専用のインスタンス(ASM インスタンス)が流行り
- テーブルやインデックスなどの永続データは表領域(TABLESPACE)と呼ばれるオ ブジェクトに保存する
- トランザクションログ(REDOログ)は複数のグループから構成される
- サイズとパスは管理者が決定し変更するためにはグループの再作成が必要
Postgre
- ストレージ領域はファイルシステムと密に結合している
- ファイルシステムの階層構造をそのままデータベースの構造に利用している
- Oracle Database や MySQLでは、レコードが更新されると更新前のレコードはブロック単位にロールバックセグメントに退避される
- 不要になるとロールバック・セグメントから削除される
- Postgreはレコードが更新されると更新前のレコードはそのまま保持され、更新後のレコードは同じブロックに追記される
- VACUUM処理により更新前のブロックは削除される
- すべてのデータはデータベースクラスタと呼ばれるファイルシステム上の単一ディレクトリに保存される
- テーブルやインデックスなどのオブジェクトはそれぞれ個別のファイルで管理する
- ファイル名はオブジェクトを示す一意なoid
- トランザクションファイルはWALファイルと呼ぶ
- ファイル名は自動的に決められる
- すべてのファイルは一定のサイズで、基本16Mで一度作成すると変更ができない
- 原則バックエンドプロセスが書き込む
- 順番に書き込まれいっぱいになったら次のファイルに名前を変えて書き込まれる
MySQL
- 複数のストレージエンジンを使えるのが特徴
- 古いのはMyISAMが使われていたが、最近はInnoDB
- ストレージはデータディレクトリに保存される
- ファイル名やディレクトリは設定で自由に変えられる
- デフォルトでデータベース名と同じ名前のディレクトリができる
- トランザクションログはバイナリログとInnoDBログがある
- バイナリログにはレプリケーションに使用する変更イベントが格納される
- InnoDB ログはトランザクションの完了と同期して書き込みが行われる
- InnoDBはOracleのように予め大きなサイズのDBを作る方法と、テーブルごとに作る方法がある
SQL Server
- 他と比べるとシンプル
- データベース単位にデータファイル を作成する
- トランザクションログは大規模な一つのファイル
SQL を比べる
- SQL 文は標準化の対象にならなかった分野に微妙な差異がある
- ISOやJISの標準化により多くの構文が同じ動きをする
- 固有の構文ができており差別化の要因になっている
関数や演算子の違い
- NULLを変換する関数名がRDBMSによって違う
- 文字列の長さの定義が微妙に異なる
- Oracleは文字の数と定義する
- Postgreは文字数だが、終端の空白は無視する
- MySQLはバイト数と定義する
- MysQL以外は0による割り算を行うと実行時エラーになる
- MySQLは0による割り算はNULLになる
- Oracleの時刻関数はマイクロ秒まではかれる。日付と時刻を返す
構文と動作の違い
実践Scala
lit-elementでTypeScriptを試す
lit-elementというWeb Componentsを作るライブラリの公式にTypeScriptでの書き方が書いてあったので試してみる。
lit-element.polymer-project.org
package.json
"devDependencies": { "ts-loader": "^6.0.4", "typescript": "^3.5.3", "lit-element": "^2.1.0", "webpack": "^4.31.0", "webpack-cli": "^3.3.2", "webpack-dev-server": "^3.3.1" }
tsconfig.json
lit-element.polymer-project.org
lit-elementのサイトにはmoduleはES2017と書かれていたが、どうやらtypescriptのmoduleはES2017を指定できないのでes2015を指定している(typescriptあまり詳しくないのでもしかしたら指定できるのかも?)
Decoratorを使うため、experimentalDecorators
はtrue
で。
{ "compilerOptions": { "target": "ES2017", "module": "es2015", "moduleResolution": "node", "lib": ["ES2017", "DOM"], "experimentalDecorators": true } }
webpack.config.js
lit-elementだからといって特別な指定はなし。
const path = require('path'); module.exports = { entry: { raw: './src/js/raw.js', lit: './src/js/lit.js', ts: './src/js/ts.js' }, output: { path: path.join(__dirname,'dist'), filename: '[name].js' }, resolve: { extensions:['.ts', '.js'] }, devServer: { contentBase: path.join(__dirname,'dist') }, module: { rules: [ { test:/\.ts$/, loader:'ts-loader' } ] } };
ts-my-element.ts
そもそもあんまりtypescriptを学んでないが、lit-elementのみで書く場合の差はcustomElements.define
とpropertyの指定の仕方くらい。
ちなみに、attributeで型の違うものを渡しても(下の例だとprop2に文字列)コンパイラがエラーを吐いたりしはしない。
普通にlit-elementで書くより行数は減る。
import { LitElement, html, customElement, css, property } from 'lit-element'; @customElement('ts-my-element') export class MyElement extends LitElement { @property({type : String}) prop1 = 'Hello World'; @property({type : Number}) prop2 = 5; @property({type : Boolean}) prop3 = true; @property({type : Array}) prop4 = [1,2,3]; @property({type : Object}) prop5 = { subprop1: 'hi', thing: 'fasdfsf' }; static get styles() { return css` p { display: block; padding: 10px; border: 1px solid #e5e5e5; } `; } render() { return html` <p>prop1: ${this.prop1}</p> <p>prop2: ${this.prop2}</p> <p>prop3: ${this.prop3}</p> <p>prop4: ${this.prop4.map((item, index) => html`<span>[${index}]:${item} </span>`)} </p> <p>prop5: ${Object.keys(this.prop5).map(item => html`<span>${item}: ${this.prop5[item]} </span>`)} </p> `; } }
typescriptをそもそもそんなに学んでいないので、ピンときていない・・・。
一応成果物
Nuxt.js + Netlify、Nuxt.js + Firebase Hostingでリリース
Netlify
➜ nuxt-sample git:(master) netlify init ? What would you like to do? + Create & configure a new site Choose a site name or leave blank for a random name. You can update later. ? Site name (optional): tiwu-nuxt-sample ? Account: tiwu official's team (personal) Site Created ? Your build command (hugo build/yarn run build/etc): npm run generate ? Directory to deploy (blank for current dir): dist ? No netlify.toml detected. Would you like to create one with these build settings? Yes
netlify init
叩いて、新しいNetlifyのSiteを作って、npm run generate
とdist
を指定して終了。
楽ちんすぎた。
PWAをオンにしてたから、Auditsも満点。
Firebase Hosting
nuxt-sample git:(master) firebase init ######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ######## You're about to initialize a Firebase project in this directory: /nuxt-sample ? Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your choices. Hosting: Configure and deploy Firebase Hosting sites === Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now we'll just set up a default project. ? Select a default Firebase project for this directory: [create a new project] === Hosting Setup Your public directory is the folder (relative to your project directory) that will contain Hosting assets to be uploaded with firebase deploy. If you have a build process for your assets, use your build's output directory. ? What do you want to use as your public directory? dist ? Configure as a single-page app (rewrite all urls to /index.html)? Yes ✔ Wrote dist/index.html i Writing configuration info to firebase.json... i Writing project information to .firebaserc... ✔ Firebase initialization complete! Project creation is only available from the Firebase Console Please visit https://console.firebase.google.com to create a new project, then run firebase use --add
What do you want to use as your public directory?
はdist
を
Configure as a single-page app (rewrite all urls to /index.html)?
はYes
を
毎回コマンド叩くときにPJ作っておくの忘れる。
netlifyはinit時に作ってくれるんだけどな・・・。
yarn generate firebase deploy
ビルドもnetlifyと比べると面倒くさい。
tiwu-nuxt-sample.firebaseapp.com
余談
とりあえず何か新しいの覚えたときにリリースまでできないと不安でしょうがない・・・。
create-nuxt-appのエラー解消
初めてcreate-nuxt-app
してみたけどいろいろエラーがでた・・・。
初めてだったからいろいろ盛ってみた。
エラー1
error eslint@5.16.0: The engine "node" is incompatible with this module. Expected version "^6.14.0 || ^8.10.0 || >=9.10.0".
どうやらnodeのversionが古く怒られたのでnodeのversionをあげて解決。
エラー2
DeprecationWarning: Tapable.plugin is deprecated. Use new API on .hooks instead
ぐぐるといろいろ出てくる。
どうやらNuxtのPWA Moduleが原因っぽい。
いろいろ調べたら下記Issueを発見。
Versionを上げれば治るらしいので試す。
自分のところだと"@nuxtjs/pwa": "^2.6.0",
だったので、"@nuxtjs/pwa": "^v3.0.0-beta.16",
に変更して、yarn instal
(5分くらいかかった)。
無事解決
エラー3
12:11 error Replace `⏎··········href="https://nuxtjs.org/"⏎··········target="_blank"⏎·········` with `·href="https://nuxtjs.org/"·target="_blank"`
今度はESLintに怒られる。
どうやらindex.vueのaタグの書き方悪いらしい。
<template> <div class="container"> <div> <logo /> <h1 class="title"> nuxt-sample </h1> <h2 class="subtitle"> My wonderful Nuxt.js project </h2> <div class="links"> <a href="https://nuxtjs.org/" target="_blank" class="button--green" > Documentation </a> <a href="https://github.com/nuxt/nuxt.js" target="_blank" class="button--grey" > GitHub </a> </div> </div> </div> </template>
1 error and 0 warnings potentially fixable with the `--fix` option.
と言われるのでyarn lint --fix
を叩く。
<template> <div class="container"> <div> <logo /> <h1 class="title"> nuxt-sample </h1> <h2 class="subtitle"> My wonderful Nuxt.js project </h2> <div class="links"> <a href="https://nuxtjs.org/" target="_blank" class="button--green"> Documentation </a> <a href="https://github.com/nuxt/nuxt.js" target="_blank" class="button--grey" > GitHub </a> </div> </div> </div> </template>
Documentation
のaタグだけ治って、GitHub
のaタグは治っていないんだけど、とりあえずこれで全エラーは解消された。
余談
ビルド中が可愛い
RUNNING LEAN
RUNNING LEANを読んだので備忘録として残す。
Running Lean ―実践リーンスタートアップ (THE LEAN SERIES)
- 作者: アッシュ・マウリャ,渡辺千賀,エリック・リース,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/12/21
- メディア: 単行本(ソフトカバー)
- 購入: 3人 クリック: 14回
- この商品を含むブログ (19件) を見る
著者はAsh Maurya。
そもそもこの本はブログに顧客開発やリーンスタートアップについて書いていたのをまとめた書籍。
リーンスタートアップの実践的な手法のガイドという立ち位置。
なお、THE LEAN SERISというエリック・リースがキューレーターを務める企画?の1冊目。
イントロダクション
- 製品の構築コストは下がったが、スタートアップが成功する確率は上がっていない
- スタートアップの多くは失敗している
- 成功したスタートアップの2/3は当初のプランを途中で大幅に変更している
- 当初のプランが優れていたのではなく、リソースを使い切る前にうまくいくプランを見つけた
- Running Leanはリソースを使い切る前に、プランAからうまくいくプランへと反復的に移行するプロセス
- 顧客の要求を収集後、リリースまで顧客から離れる
- この離れている期間が問題
- 車を作る際に顧客に要望を聞いていたら「速い馬が欲しい」と答えていただろう
- つまり、顧客から本当に欲しいものを聞き出すのは難しい
Running Leanとは
- 速度・学習・集中
- 顧客の行動を計測して、ビジョンをテストする
- 製品開発サイクルを通じて、顧客と関係を築く
- 短いイテレーションで製品と市場の検証を同時に行う
- 規律のある厳格なプロセス
- 数々の方法論や思想家を参考にしている
顧客開発
- スティーブ・ブランクが作った
- 製品開発サイクルにあわせて、顧客との継続的なFBループを構築する
リーンスタートアップ
- エリック・リースが提案
- 顧客開発とアジャイルソフトウェア開発とリーン手法を統合したもの
- リーンは無駄を省くという意味
- 単位時間あたりの顧客に関する学習量の最大化が目的
ブートストラッピング
- ビジョイ・ゴスワミが提唱
- 顧客からの収益で資金をまかなうという意味
メタ原則
- 原則は何をするか導く
- 戦術はどうするか示す
プランAを文章化する
- 強いビジョンと実現するプランAがあるが、ビジョンは信念で裏付けされている
- ビジョンを事実で裏付けする必要がある(ビジョンはテストされていないので
- 最初にすることはビジョンを書き出して、誰かと共有をする
- 1枚にすべてが収まるリーンキャンバスがおすすめ
- 数時間で書ききることができる
- 短い文章で書く必要があるので、本質が抽出できる
- 1ページなので共有も楽
- ソリューションの面積は1/9しかない
- 起業家はソリューションに目を向けがちだが、顧客はソリューションには興味がない
- 最高のソリューションを作るのではなく、ビジネスモデルの全体を把握しまとめることが大事
- ビジネスモデル自体を製品と扱う
- リーンキャンバスとはビジネスモデルを9つの部品に分解し、各項目をテストするもの
プランで最もリスクの高い部品を見つける
- ソリューションは基本的に最もリスクの高いものではない(製品はなんとか作ることができる
- 最も大きいリスクは「誰も欲しくないものを作る」こと
- 「課題・解決フィット」→「製品・市場フィット」→「拡大」
課題・解決フィット
- 解決に値する課題はあるか?
- それは顧客が必要としているか?
- 顧客は金を払うか?誰が金を払うのか?
- 解決可能か?
製品・市場フィット
- 必要とされるものを構築したか?
- MVPがどれだけ解決しているかテストする
拡大
- どうやって成長を加速させるのか?
ピボットと最適化
- 「課題・解決フィット」、「製品・市場フィット」と「拡大」で分ける
- ピボットとは学習し方向性を変更すること
- 最適化はプランを加速すること
- 最も学習できる状態は50%の状態
- そのため大胆な成果を狙って学習をするべき(小さいテストは後で
プランを体系的にテストする
Running Leanの実例
- この書籍を書いたときの例
いかにして書籍を執筆反復したか
- 何人かに本を書いてと言われたが無視していた
- たくさん言われ始めたので本を書くことを考えた
- 読者に電話をして、どうして本を書いほしいか聞いた
- 本書のUVP(独自の価値提案)を理解する
- 顧客開発・リーンスタートアップの実践に苦労している人が多い(課題)
- ブログを段階的なガイドとして読んでいる(ソリューション)
- webの製品を開発している(アーリーアダプター)
- 目次と書名と書影のサイトを構築
- 本のリスクの高いところは目次
- 顧客に目次を聞いてFBもらって、目次を改良した
- しかし顧客が10人くらいなので、ブログで告知した(解決に値する課題ではなかった)
- 1000個のメアドが集まったので、先に進むことを決めた
- ブログのコピペをしようとしたが満足できるものにはならなかった
- 目次をもとにワークショップを3回行い、中身を改良していった
- 2週間ごとに2章ずつPDFでリリースする方式をとった
- 半分は好きな端末で読みたがっていた
- しかし、半分(アーリーアダプター)は同意した
- 2週間ごとにリリースし、FBをもらい、推敲して誤字を修正していった
- 実際に1000人くらいに売れてから、出版社からお金をもらい、出版した
リーンキャンバスの作成
見込み客を考える
- 最初はぼんやりとしたアイデアしかない
- この時点で製品を作ったり、ビジネスモデルを決めるのは危険
- 複数のモデルを同時にテストしていく
- 顧客はお金を払うが、ユーザーはお金を払わない
- 顧客のセグメントを細かく分ける
- 最初は一枚のリーンキャンバスを書く
- その後に顧客セグメントごとにリーンキャンバスを書く
リーンキャンバスをスケッチする
- 一気にスケッチする(15分以内くらい)
- 正しいことを書こうと議論するより、適当に書くか、空欄にする
- リスクが高いことがわかる
- 短く書く
- 現在の分かる範囲で書く
- リーンキャンバスの書き方は顧客主導型で
- ターゲットとなる顧客セグメントに対して、課題を1~3位まで書く
- もしくは、顧客に必要なジョブ(作業)の観点から課題を考える
- 代替品を列挙する(オンラインコラボツールの代替品はEメール)
- ユーザーを特定する
- UVPを決める
- ソリューションはゆるく書く
- 課題とソリューションの統合はできるだけ遅らせる
- チャネル(顧客への経路)
- 収益の流れは最初から考える
- 学習時は大勢の顧客はいらない
- 課金のシステムを考えているなら最初から実装して課金させるべき
- 価格は製品の一部(似たような水でも高いほうが品質が良いと感じてしまう)
- 価格が顧客を決める
- 課金は難しい行為だが、早めに検証ができる
- コスト構造をリストにする
- 主要指標はAARRRで見る
- 獲得(Acquisition)は何も知らない訪問者が、見込み客になった時点を表す
- 花屋なら店に入ること
- ウェブサイトは登録ページを見た訪問者と定義(サイト次第
- アクティベーション(Activation)は見込み客が満足の行くユーザー体験をした時点を表す
- 花屋なら入った後店がしょぼかったら駄目
- ウェブサイトは使ってみて期待(UVP)と製品を結びつける
- 定着(Retention)は製品の反復利用
- 花屋ならまた来る、サイトならまた来る
- 収益(Revenue) は課金イベント
- 花の購入やウェブなら製品の申し込み
- 紹介(Referral)は口コミとか
- 圧倒的優位性は最も難しい
- 機能数とかは関係ない
- 先行性もあまり関係ない
- コピーする価値あるものはコピーされる
- 本当に優位性のあるものはコピーされずらい
- 内部情報、専門家の支持、ドリームチーム、信頼性、巨大なネットワーク、コミュニテイ、既存顧客、SEO
ビジネスモデルの優位性
- リスクの優先順位を間違えるのは最も無駄
リスクとは
- 不確実は複数の結果が存在する状態
- リスクは好ましくない状態になりえる不確実な状態
- 機会費用と実質費用の両面から数値化し、優先順位をつける
- 本の場合は価格がリスクではない
- 良い本を書けば買われないリスクは下がる
- なのでリスクは顧客が欲しい良い本が書けるか否か
- 製品リスク:正しい製品を作る
- 顧客リスク:顧客への経路を作る
- 市場リスク:実現可能なビジネスを作る
- 計測や定量化は確率統計的手法を利用
- インタビューなど定性的計測はダグラスハバードがおすすめ
ビジネスモデルの比較
- 十分に大きな市場を持ち、製品の周りにビジネスを構築でき、必要とする顧客に近づけるビジネスモデルを見つけるのが目的
- 顧客の不満レベル(課題)
- 顧客セグメントに優先順位をつける
- 近づきやすさ(チャネル)
- 価格と粗利益(収益の流れとコスト)
- 市場規模(顧客セグメント)
- 技術的実現可能性(ソリューション)
- いろいろな顧客セグメントごとにリーンキャンバスを書いて、どの顧客セグメントから検証するか決める
- 決定したら誰かに見てもらう(アドバイスを鵜呑みにしてはいけない
- リーンキャンバスを見てもらおう
- 2割説明して残りは聞き出すのに時間を使う
- 最もリスクが高いと思われるのはどこか?
- 似たようなリスクを克服したことはありますか?
- リスクをどうやってテストしますか?
- 他に話を聞いたほうがいい人はいますか?
実験の準備
課題チームと解決チームを作る
- 部署をぶち抜く
- 課題チームはユーザーインタビューやユーザビリティテストなど
- 解決チームは開発・テスト・リリース
- メンバーが重なっても良い
- 1つのチームで両方の役割を担っても良い
- 顧客とのやり取りは全員の責任
- 小規模でなく最小でチームを作る
- コミュニケーションが楽でコストが低い
- 課題と解決のバランスが大事
- 開発・デザイン・マーケの3つがチーム作りで必要
- 基本外部委託しないほうがいい
効果的な実験
- 最適な実験をするには、速度・学習・集中が必要
- 速度と集中しかないと高速に学びのないところをぐるぐる回るだけになる
- 学習と集中しかないと速度がないのでリソースを使い切ってしまう
- 速度と学習しかないと早すぎる最適化をしてしまう(製品がないのにLPを最適化したり
- 実験するときは1つの指標と目標にする
- 反証可能な仮説にする(間違いがはっきりわかる文)
- あいまいな仮説「専門家になってアーリーアダプターを集める」
- 反証可能な仮説「ブログに投稿して100人集める」
- 最初は強いシグナルを受け取る(5人の顧客のインタビューで十分)
- 肯定的な意見は仮説を定量的データで検証してもいいということ(定性的から定量的へ)
- 計測結果を具体的で反復可能な行動に結びつけて製品を変更するのは難しい
- インタビューはパターンが見えるまで同じ方法を繰り返す
- 定量的検証はコホート分析やスプリットテストなどを使う
- わかりやすいダッシュボードを作る
- コンバージョンダッシュボードとリーンキャンバスなどを1つのダッシュボードで管理する
イテレーションのメタパターンをリスクに適用する
- 中途半端な学習や否定的な学習でやる気を無くす
- 肯定的な学習から楽観的になってしまう
- 正しい製品かつ拡張性のあるビジネスモデルを作るのが大事
- 学習自体を目的に実験せず、目標のための実験にしていく必要がある
- 単位時間あたりの学習量(どのリスクが高いか)を最大化する
- 課題を理解→ソリューションを決定→定性的に検証→定量的に検証
- 製品リスク:正しい製品を作る
- 解決に値する課題かどうか確認する
- MVPを決める
- MVPを構築し、小規模に検証する(UVPのデモ)
- 大規模に検証をする
- 顧客リスク:顧客への経路を作る
- 不満を持っている人を特定する
- 製品を今すぐ欲しいと思うアーリーアダプターに範囲を狭める
- アウトバウンドチャネル等から始める
- インバウンドチャネルの構築
- 市場リスク:実現可能なビジネスを作る
- 既存の代替品から価格を設定する
- 顧客の声を聞いて価格をテストする(口約束)
- 顧客の行動を聞いて価格をテストする
- コスト構造を最適化する
顧客インタビューへの準備
学習するには顧客と話すこと。
アンケート調査もフォーカスグループも不要
- アンケートは正しい質問があることが前提
- 正しい質問を作るのは難しい
- 顧客インタビューとは何がわからないのさえわからないことの探求
- アンケートは正しい答えがあることが前提
- 正しい答えの選択肢を用意するのが必要
- 最初は「自由回答形式」の質問を使って学習する
- アンケートはボディランゲージが見えない
- フォーカスグループは「集団思考」を形成してしまうのが問題
- アンケート調査は顧客インタビューから学習したことを検証するのに効果的
- 統計的有意性の実証に使う
人と会話するのは難しい
- ピッチではなく学習を中心に考える
- ピッチの問題点は顧客が「正しい」製品の知識を持っている前提になっていること
- 「正しい」ソリューションの前に、「正しい」課題を理解する必要がある
- 顧客に何が欲しいか効かず、行動を観察する
- 嘘つくかもしれないし、儀礼的に答えることもあるので、インタビュー中に行動から言葉を検証する
- 口約束ではなく行動を示す(課金など
- 台本に合わせて会話をする
- 広く網を広げて局所化を防ぐ
- サンプルを厳選するのではなく、結果から厳選をする
- インタビューは直接会うこと
- いきなり知らない人でなく、知り合いから始める
- 誰かと一緒に行って、第三者目線を取り入れる
- コーヒーショップなど中立的な場所でやる
- 謝礼などはしない(お金を払う客を見つけるのが目的なので)
- 録音をすると自意識過剰になったりするので、録音は基本無しで
- インタビュー後すぐに文章化をする
- 4~6週間で30~60人インタビューをする
- スケジュール調整は無駄な作業なので委託しよう
見込み客を探す
- 知人バイアスがかかるが、誰とも話さないよりはいいので知人から
- 紹介による顧客探し
- 親近感は大切なので地元で探す
- 予告ページでメアドを登録してもらう
先制攻撃と反論
- 顧客インタビューに対する反対意見
- 顧客は自分の課題を把握していない
- 数10人と話しても統計的に有意がない
- 定量的指標で十分
- 自分が顧客だから、誰かと話す必要はない
- 友達が良いねと言っている
- 週末でなにか作れるのに、インタビューする必要はあるのか
- すでに課題を把握しているからテストは不要
- 課題がわからないのでテストができない
- 誰かにアイデアを盗まれる
課題インタビュー
学習すべきこと
- 製品リスク:何を解決するのか?
- 市場リスク:競合は誰なのか?
- 顧客リスク:誰が困っているのか?
課題のテスト
- 最初は課題に対する顧客の反応を計測すること
- 現時点で解決できているか?どのように解決しているのか?
反証可能な仮説
- 反証可能な仮説 = 具体的で反復可能な行動 → 期待する計測可能な成果
課題インタビューの実施
- 歓迎、顧客情報の収集、ストーリーの伝達、課題の優先順位、顧客の世界観の探求、まとめ、結果の文章化
- 歓迎:インタビューの流れを簡単に説明する
- 顧客情報の収集:アーリーアダプターを選定するために、顧客情報に関する基本的な質問をする
- ストーリーの伝達:上位の課題をストーリーで説明する
- 課題の優先順位:課題を1 ~ 3つ選んで、見込み客に優先順位をつけてもらう
- 顧客の世界観の探求:インタビューの中心
- まとめ:引き続き興味を抱いてもらえるようなフックを提供と今後の協力のお願い
- 結果の文章化:記憶が新しいうちに文章化する
課題を理解していますか?
- 結果を週次でレビューする
- アーリーアダプターから始める
- 最も賛同してくれる顧客セグメントを特定する
- 課題を洗練する
- 必要ないというシグナルを受け取ったら削除して、絶対に必要と受け取ったら追加する
- 既存の代替品を理解する
- アーリーアダプターは既存の代替品と比較する
- 顧客が使う言葉に注目する
- UVPの鍵となる単語は顧客の言葉に耳を傾ける
- 最低10人にインタビューをして下記が可能になれば終了してもよい
- アーリーアダプターとなる顧客が特定できた
- 「絶対に必要」な課題が見つかった
- 現在の顧客の解決方法がわかった
ソリューションインタビュー
- 製品を作る前にデモを使ってソリューションのテストをする
学習すべきこと
- 顧客リスク:誰が困っているのか?
- 製品リスク:課題をどのように解決するのか?
- 市場リスク:どのような価格モデルにするのか?
ソリューションのテスト
- デモは実現可能でなければいけない
- デモを最終的な製品で使用しない技術を使って作ってはいけない
- デモは本物に見えないといけない
- デモは高速に反復する必要がある
- インタビューの結果をすぐデモに反映して、次のインタビューでテストできるようにする
- デモは無駄を最小化する
- デモは本物に見えるデータを使う
- デモはYotutubeとかに出しても良い
価格のテスト
- 価格のことは顧客に聞かず、ただ伝えるだけ
- 適正な価格は説得することができる
- 価格は顧客セグメントを定義するもの
- 登録の障壁を下げずに上げる
- 希少性:どっちつかずな100社より、やる気のある10社に注力をする
- アンカリング:価格は相対的だが、新しいカテゴリの製品は別の製品と比較して安く見せる
- ソリューションインタビューのAIDA
- 認知(Attention):効果的に認知を集めるためには、顧客の課題を突きつける
- 興味(Interest):デモを使って興味を引く
- 欲求(Desire):欲求は段階的に引上げる
- 行動(Action):製品に対する口約束・書面の契約・前払い金をとりつける
- ピッチは「オール・オア・ナッシング」の提案
- フレーミングは仮説を使って顧客の反応を引き出す
ソリューションインタビューの実施
- 既存の見込み客にお願いする
- 課題インタビューのときに今後の協力を依頼しておく
- 新規の見込み客を入れる
- 歓迎、顧客情報の収集、ストーリーの伝達、デモ、価格の検証、まとめ、結果の文章化
解決に値する課題はあったか
- 機能を追加・削除する
- 抵抗がなければ価格を上げる
- ソリューションインタビューは以下の革新が持てたら終了
- アーリーアダプターの顧客情報が特定できた
- 「絶対に必要」な課題がわかった
- 課題を解決するのに必要な最小限の機能が定義できた
- 顧客が支払ってくれる価格がわかった
- (概算で)うまくいきそうなビジネスがわかった
バージョン1.0をリリース
製品開発は学習の邪魔
- 開発やQAは学習できていない
- リリースまでのサイクルを短くして学習を加速させる
MVPの縮小化
- MVPのスコープを減らすということは、製品メッセージを希釈する不純物を取り除く
- 白紙から始める:機能を追加するたびに正当性を検討する
- 最上位の課題から着手する
- 「あれば嬉しい」「必要ない」機能は削除する
- 初日から課金するようにする(ただし、徴収は30日後)
- 最適化ではなく学習に集中をする
- 継続的デプロイ
アクティベーションの流れを定義する
- 顧客がサービスに登録をしてから最初の体験に満足するまでの道筋
- 顧客に速くUVPを体験してもらうこと
- 登録の障壁を下げても学習を犠牲にしてはいけない
- 手順を減らしても学習を犠牲にしてはいけない
マーケティング用のサイトを構築する
- 売るのを目的とする
- 最初の8秒が大切
- 概要ページで「会社」から製品を購入する理由を作る
- 利用規約プライバシーポリシー
- 製品ツアーページ(スクショなど
計測の準備
- 顧客ライフサイクルは可視化するだけでなく、計測できる必要がある
行動につながる指標
- 製品/市場フィット前の目的は、CVの最適化ではない
- 顧客ライフサイクルにおけるホットスポットの特定と、その解決
- インタビューなどでは、顧客の声に耳を傾けたが、これからは顧客の行動を計測する
- 行動につながる指標 = 観測結果を具体的で反復可能な行動につなげるもの
- DL数やPVは製品の現状を表すが、単体では次に何をすればいいかわからない
- これらは、計測対象ではなく、計測方法
指標は人が重要
- 評価尺度の実態は人である
- 数字の裏にいる人に話しかける必要がある
- 理想的なCVダッシュボードは、分析とカスタマーリレーションシップマネジメント(CRM)を併せ持つ
- 指標はそれ単体では何も説明しない
- 指標はどこが間違っているか教えるが、なぜ間違っているかは教えてくれない
- ユーザーの方からやってくることはない
- ユーザーはソリューションを丁寧に見てはいない
- うまくいかないことがあれば、すぐ不信感を抱いて、興味を失う
- すべての指標は等価ではない
- 多くのユーザーやBOTが来るので、指標を異なるバケツに分ける必要がある
単純なファンネルレポートは十分ではない
- ファンネルレポートはLPのCVのようなミクロレベルに適している
- 6月の、獲得、アクティベーション、収益のCVファンネルレポートがある場合
- 収益は5月の数値になるので、歪みが出る
- 7月の登録数が減ると、CVRがあがるかもしれない
- 新機能のリリースなどと結びつけるのが難しい
- いずれのファンネル分割が発生する
コホート
MVPインタビュー
- MVPの販売の前にアーリーアダプターに直接販売をして、デザインや価格などを改良する
学習すべきこと
- アーリーアダプターに20分インタビューで説得できないのであれば、LPに来た訪問者を8秒で説得できない
- 製品リスク:製品の魅力はなにか?(独自の価値提案)
- 顧客リスク:十分な顧客はいるか?
- 価格は適正か?
MVPインタビューの実施
- 歓迎、LPの提示、価格ページの提示、登録とアクティベーション、まとめ、結果の文章化
顧客ライフサイクルの検証
フィードバックを楽にする
- 顧客から学習する近道は、顧客に話しかけること
- 気にかけていることが伝わる
- 問い合わせが多すぎるという問題はない
- テクニカルサポートは継続的な学習のフィードバックループ
- テクニカルサポートは顧客開発
- テクニカルサポートはマーケティング
- 投票ツールによるフィードバックは使わない
試用期間中に問題を解決する
- 試用期間は顧客ライフサイクルを制限できる
- 試用期間の最初の目的は離脱を減らす、次に定着やエンゲージメント、課金
獲得とアクティベーション
- 優先事項:学習できるだけのトラフィックを稼ぐ
- ファンネルを掘り下げる
- どこかで離脱をしていないか?パターンを探す
- 離脱したユーザーをリストで持っておき、分析をする
- よきせずエラーをキャッチして通知させる
定着
- 優先事項:試用期間中にユーザーに戻ってきてもらって、使ってもらう
- 丁寧なリマインダーを送信する(メールなど)
- インタビュー相手に協力を求める
収益
- 優先事項:課金
- 決済システム
- 支払った顧客に電話をする
- (売りそこねた)見込み客に話を聞きに行く
紹介
- 優先事項:推薦の声をもらう
ローンチの準備
- 結果を頻繁にレビューする
- 最も重要な課題から着手する
- 可能な限り小さなことをやる
- 確実に改善する
- コンバージョンダッシュボードを監視する
ローンチ条件
- アーリーアダプターの80%がコンバージョンファンネルをうまく抜けたらOK
機能の押し売りをやめよう
機能は押し付けず引っ張ってもらう
- 追加機能は独自の価値提案(UVP)を薄める
- すぐにMVPに見切りをつけてはいけない
80/20ルールを実施する
- 80%既存機能の計測と改良
- 20%新機能の開発
機能の流れを抑制する
- かんばんで管理する
- バックログ
- MMF(市場価値最小限の機能:Minimum Marketable Feature)とバグを区別する
- 作業中
- 作業中のタスクの数を制限するのが大切
- 完了
- ほんとうの意味での完了は、顧客の検証により学習ができること
機能要求を処理する
- Getting Things Done方式のワークフロー
- 正しい行動で、適切な時期か?
- 小さな機能、またはバグか?
- 緊急か?
機能ライフサイクル
製品/市場フィットの計測
製品/市場フィットとは
- 市場を満足できる製品がある状態
ショーン・エリスのテスト
- 製品が使えなくなったときにどう思うか?
- 非常に残念、少し残念、残念でない、使っていない
- 40%が非常に残念と答えたならよい
- アンケートは学習より、検証に効果的
- 統計的有意性を得るためにはサンプル数が膨大である
「適切な」マクロ指標に集中する
- 一度きりのサービスはサービスの体験が重要
- アクティベーションの指標で計測できる
- SaaSのような何度も使うサービスも体験が重要
- 本当の成功は繰り返しの利用
- 人が欲しがるものを作る指標は、定着である
- アクティベーションの済んだユーザーの40%が定着すれば、トラクションがあると言える
収益について
- 価格は製品の一部
- ただし、収益は検証の1つの形にすぎない
- 収益は最初の検証だが、定着は究極的な検証
人が欲しがるものを作ったか
- コンバージョンダッシュボードを毎週レビューする
- 目標とバックログの優先順位をつける
- 大胆な仮説を作る
- 機能を追加・削除する
- 価値指標を監視する
- ショーン・エリスのテストを実施する
- 40%のユーザーが定着・ショーン・エリスのテストを通過したら初期は終了
製品/市場フィットにおける市場
- 解約率、ウィルス係数、顧客獲得コスト、顧客生涯価値などがビジネスモデルを拡大させる要因
- 初期のトラクションを実証する前に、ビジネスの拡大をするのは無駄
- 3つの成長のエンジン
- 粘着型:高い定着率
- ウィルス型:高い紹介率
- 支出型:高い利益率
- 価値指標の検証から着手する
- 顧客の製品に対する態度を理解する
- 調整するエンジンを選択する
ネットワーク効果のある製品
- 製品の数はユーザーの数
- 定着は王様
- 成長させる前に検証
マルチサイド製品
- 売り手がいないと買い手がいなく、買い手がいないと売りてはいない
- 両サイドのキャンパスを作る
- ミクロ単位で仮説検証をする
- 仲介処理の自動化は後
結論
拡大
- 製品/市場フィットの達成が最初のマイルストーン
- 重点が学習から、拡大になる
- 成功の指標を使って進捗を計測し、成功の確率をあげる
低燃費スタートアップの作り方
- ブートストラッピングは外部資本なしに企業を設立する(正しい行動を適切な時期に)
早すぎる資金調達は無駄なのか
- 資金調達は検証ではない
- 検証がなければ大きくできない
- 投資家は進捗を別の方法で計測する
- 資金調達は思っているよりも時間がかかる
- お金を持つとダメになる
- アドバイスや人脈は?
- 製品/市場フィットまでどうやって生き残ればいいか
- 本業は続ける
- バーンレートを節約しよう
- 初日から課金しよう
- 関連する商品を売ろう
リーンスタートアップにおけるフローの達成方法
- リーンスタートアップの基本原理は、無駄の削除
- 建物の外の活動は創業者が参加する
- 建物の中の活動も参加する
- 2つのスケジュールの綱引き問題が発生する
- 綱引きの均衡点を見つけるときに、フローという概念が約立つ
- 絶好調な精神状態を表す:フロー状態
- 明確な目的がある
- 高度な集中力を必要とする
- 中断や妨げるものがない
- 明確で即時的なFBがある
- やりがいがあって挑戦できる
毎日のフローを作る
- 予定しているクリエイターの活動
- 予定しているマネージャーの活動
- 予定していない活動
- クリエイターの仕事は中断されない時間を作る
- クリエイターの目標は早い時間に達成する
- マネージャーの仕事は遅い時間に設定する
週単位のフローを作る
- 顧客開発に最適な曜日を見つける(火〜木)
- 顧客の休止時間にクリエイターの仕事を入れる(月、金)
ソフトウェアの無駄を排除する
- 製品は顧客にプルしてもらう
- 顧客が要求するまでは製品を作らない
- 80%は既存機能の最適化に費やす
SaaS製品の価格設定
- スタートアップの最初の目標は、最適化ではなく学習
- 最初は「無料のお試しプラン」だけを用意する
- 最初は料金プランは1つ
- コストを考慮に入れる
フリーミアム
- 無料で提供し、顧客獲得後付加価値サービスをつける
- コンバージョンが低い
- 価格設定は売り手ではなく、買い手の気持ちが決める
- 検証サイクルに時間がかかる
- 間違った指標に集中してしまう
- 無料ユーザーは無料ではない
- フリーミアムの有償部分から始める
- 優れた無料プランは、無料体験版のようでなければいけない
予告ページの作り方
- 興味のない訪問者に興味のある見込み客になってもらう
- 大きな約束をする(UVP)
- 即効性のある明確な見出し = 顧客が望む結果 + 明確な期限 + それが達成されなかったときの代替案
- 顧客に結びつけて考える(課題)
- 関心と欲求を生じさせる(ソリューション)
Ginza.js #1 で登壇してきた #ginzajs
Meguro.es, Gotanda.js, Roppoingi.jsに続き、Ginza.js!
ご当地JSシリーズ的な感じになってきた
Ginza.js
# 目指す価値 - 主にJavaScript関連技術について、様々な人から多様なトピックに関する発表や議論が生まれるようにする - そのために、開催頻度を高く維持し、なるべく多くの人に発表機会を提供する - そのために、なるべくイベント形式をシンプルにして、開催コストを最小限にする # シンプルさを保つために、なるべくやらないこと - 参加費を取ったり、お金を管理したりすること - 飲食物をまとめて発注し提供すること - 会場提供以外のスポンサーを募ること
目指す価値、シンプルさを保つための方針を明示的に書いてあるのいいなと思った
会場は株式会社プレイド。
芝生があってお洒落味が強かった
芝生すご#ginzajs pic.twitter.com/bEyqpiAhUa
— Taguchi Wataru (@tiwu_official) 2019年5月22日
LT後に、登壇者を囲んで座談会をするという形式。
今回は前職の知り合いも来ていて、一緒に聞いていた。
LT
LTはVue.jsとFirebaseでサイト作成、チャートライブラリ、StoryBook、LineBot、Vue Pulgin等々
僕はweb componentsの話を。
私定時で帰りますの種田コスプレしてきましたと言ったらウケたので大満足のLTでした
Web Componentsの盛り上がりを感じたい
Gotanda.js #11 in MobileFactory で登壇してきた #gotandajs
1年ぶりに帰ってきたGotanda.jsで登壇してきました。(僕は初参加)
人生初の五反田駅。
Gotanda.js
会場はモバイルファクトリー
ありがとうモバイルファクトリー!#gotandajs
— Taguchi Wataru (@tiwu_official) 2019年5月10日
乾杯してビール飲みながらスタート。
LTはトップバッターでした。
Meguro.esとかでは勢いに任せた資料でしたが、今回はWeb Componentsの基本的なところや、最新のブラウザ関連の情報を載せました。
そのためギリギリの7分でした。
詰め込み過ぎたらスライド飛ばしたりしてギリギリ7分だった#gotandajs
— Taguchi Wataru (@tiwu_official) 2019年5月10日
グーグルスライドにコードを埋め込むときのいい方法を知りたい。
LT
他のLTはNode.jsの再起検索やGatsby.jsやdockerでnodeの開発の話やAWS CDKの話など割とサーバーサイド?よりの話が多かったかなと感じます。
全然知らない話が多くて、AWSの設定?をJS(TS)で書けるあたり、もう何でもありだなJS・・・とか思う
懇親会
休憩中にピザは消滅
ピザった🍕#gotandajs
— Taguchi Wataru (@tiwu_official) May 10, 2019
懇親会以前にピザの気配が消えた #gotandajs
— 猫鳩柔工業 (@nekobato) May 10, 2019
最後に
勉強会を継続して開催するのはかなり大変なんだろうな・・・