TECH PLAY

株式会社mediba

株式会社mediba の技術ブログ

167

こんにちは。創造開発部 兼 ものづくり推進部の森竹です。 バックエンド開発を担当しています。その他にもアジャイルの推進や BIT VALLEY -INSIDE- のコミュニティ運営に参画しています。 今回は毎週開催している読書会について、記事にさせて頂きました。 出典:カイゼン・ジャーニー たった1人からはじめて、「越境」するチームをつくるまで なぜ読書会を開催するのか 今回は読書会の題材として、「 カイゼン・ジャーニー たった1人からはじめて、「越境」するチームをつくるまで 」を選びました。 この書籍から、目的のために自分から行動して行くことを改めて学びました。またアジャイルを推進したり、自分が楽しいと感じることを共有して伝えたいとも思いました。読書会を通じて、「自分と同じように感じて欲しい」、「アジャイルを推進するのに最適な書籍だ」と考えたためです。 読書会の内容 読書会のやり方ですが、 ⻘木将幸ファシリテーター事務所 『 「8分読書会」の進め方 』 を参考にさせて頂きました。 こちらのやり方ですが、2018-10-31(水)にヴァル研究所にて開催された、 DevLOVE 主催のイベント、「 組織での読書会の開き方 」に参加し、知見を得ることが出来ました。 8分読書会の特徴は下記となります。 事前に読んでこなくていいので楽! 当日8分だけよんで、皆と意見交換できるので、手軽 他の人の視点をもらえるので、複合的にこの本を楽しめる。本を読み進める楽しみが増す 読書会参加者ですが、今回は様々な職種でエンジニア、デザイナー、プロダクトオーナー、ディレクターなどです。 読書会の手順 読む8分 書籍を1話ずつ読んでいきます。1話毎にページ数は異なりますが、読む時間は変更しません。読み方も1話内であればどこからでも構いません。 書く5分 付箋とペンを用意し、思ったことや感じたこと、気になったことや疑問点など、なんでも付箋に書きます。 語る8分 付箋をホワイトボードに貼りながら、付箋に書いた内容を自分の言葉で語ります。 全体討議 付箋が貼られたホワイトボードを眺めながら、参加者同士で話します。 読書会を開催した感想 8分間限定で読書することも大事だと思いますが、読んだ直後にアウトプットする、アウトプットしたものを自分の言葉で語るのが重要だと感じました。 アウトプットや語ることで理解が深まったり、新たな気付きがあったりします。一度読んだ書籍でも自分の視点を見直したり、他の方の様々な視点に気付きがありました。またお互いの価値観を知ることで、チームビルディングにも役立ちそうに感じました。実際のスクラムチームなどで読書会を開催するのも良さそうです。 全体討議では、実際のプロジェクトの話まで踏み込むこともあり、良いカイゼンの場になればと思いました。そのような時は、自分なりのアジャイルなエッセンスを加えてお話しするように心掛けています。 読書会のカイゼン 最初のうちは固定メンバーで開催していましたが、途中からゲストの方をお招きして読書会を体験してもらう取り組みを始めた。 既存メンバーにとっても更に異なる視点に気付きがあったり、ゲストの方本人にも効果があるように感じています。 最後に 読書会のやり方や手順、感想やカイゼンについて紹介させて頂きました。 題材である「カイゼン・ジャーニー」ですが、まだ読み終えておらず引き続き読書会を開催して行きます。 今後はもっと読書会の魅力を伝えたり、より多くの方に参加して頂いたり、読書会の文化を作って行きたいと思っています。 宣伝 BIT VALLEY -INSIDE- Vol.9 を下記日時・場所で開催します。 日時:2019-6-4(火) 19:30 ~ 21:30 場所:株式会社mediba 内 カフェスぺース「8cafe」 テーマは 「正しいものを正しくつくれているか?」〜経産省プロジェクトでのアジャイル開発〜 となりますので、是非ご参加下さい。 ※今回は DevLOVE との初共催となります。
アバター
こんにちは。創造開発部兼ものづくり推進部の武田です。 すっかり春も終わり初夏の兆しでもうすぐ梅雨と、前回の更新からだいぶ日が空いてしまいました。 今日は最近プロジェクトで試行錯誤している最中の、 モブプログラミングの取り組み についてご紹介します。 まだまだ絶賛試行中ですが少しずつ見えてきたものがあるので、 モブプログラミング(もしくはモブワーク)に取り組んで、私が感じた誤解や向き合い方についての話 をします。 モブ{プログラミング,ワーク}って? 「挫折した人にも読んでほしい」日本の第一人者に聞く、「モブプログラミング」の魅力とは? - エンジニアtype | 転職type より引用します。 3名から5名程度のエンジニアが1つのモニター、1つのPCを共有して行う開発手法。具体的には実際にコードを打ち込む「ドライバー」役が1人、それ以外は指示を出す「ナビゲーター」役となり、意見を交わしながら開発に取り組む。ドライバーは数十分おきに交代し、モブプロへの途中参加も途中退出も自由。もし作業中に問題が生じれば、その都度話し合って解決するため、手戻りが少なく、短時間でプログラミング品質の向上が見込める。 …良さそうじゃないですか? 直近プロジェクトでチームにおける学習とはということをよく考えており 、実際にやってみようと思ったわけです。先日弊社に楽天株式会社から及部敬雄さんをお招きして、モブプログラミングについて勉強会を開催しましたのでそちらの資料もぜひ参照ください。 小さなチーム、大きな仕事を実装するモブプログラミング 実はわたしはこの勉強会には参加しておりません。資料と当日の話をチームメンバーから共有してもらったのみで、事前情報はあまり持ち合わせず特別ノウハウを体系的に学んだわけではないというのをご承知おきください。 まずはやってみるフェーズ What: やること テストアフターとなったユニットテストのコード追加 jest を使ったテストコード ※ jest 経験者はメンバーのうち2名しかいない Who: メンバー全員(9人) ドライバーを2名として任意のタイミングで交代 ほかは全員ナビゲータ When: 1.5時間 Where: 会議室 How: VSCode LiveShare でコードシェアし全員端末を開いている状態 これだけ決めて最低3回はやってみようと決めていました。時間が短かったりメンバーが多かったりするのは、この人数・時間でどういった感覚なのかをまずは確かめるためです。やってみないことには何も分かりません。 この段階ではノウハウが全くないので、まずはやること事前に決めたり準備したりメインのファシリテーターを私がやったりなど、個人が場を整える準備をしてから臨んでいます。3回目で私はファシリテートを辞める、というアクションなども加えてやってみました。 初回〜3回目:やってみて感じた課題 「終わった後に進捗や成果が気になってしまう」 ToDo リストを粗方でも先に作ってからやった方が進捗の見える化ができて、消化している感が醸成されて良さそう 何となくだけど本日のゴールを決めながらやると良さそう 以上のような感想がありました。ゴールを決めるのはもちろんなんですが、どうしても終わった後に今日の成果が気になったり、タスクが完了しきれなかったことが心残りになったりします。この人数を招集してこの成果か…という具合で心理的負担はあって、どうしてもコスト高と最初感じてしまいました。 「首が痛い」 対面式で椅子が並んだ会議室でやったため、全員モニターを見ようとすると横を向くことになるので首を痛める、ということが分かりました。外的な環境がめちゃくちゃ大事そうです。 「わからないことを質問していいのか迷う」 質問すると作業が止まっちゃうから申し訳ない気持ちになるという意見がありました。それだけではなく、1.5時間 10人近いメンバーでやってるとほとんど言葉がないメンバーも出てきます。 「ドライバーの役割問題」 ドライバーやっていても話したくなってくる, 一定時間話さないのは辛い ドライバーが進めてしまうことが往々にしてあるので徹底したほうが良さそう 上記のような感想もありました。これについてはルールを徹底し過ぎたのとそもそも誤解があったようです。 ドライバーは質問するが自ら進めずナビゲーターの指示通り一字一コードをタイプするような誤解 を持って取り組んでしまっていました。 「踏みまくったバッドプラクティス」 人数が多いことで話が発散して ドライバーが混乱する テストケース名のような日本語の問題で bikeshed discussion になりがち 適宜休憩取らないとコミュニケーションが多いのでかなり疲れる 上記のような感想からわかるように、結構悪い部分を踏んでいったなと3回目を終えたあたりで感じていました。 改善フェーズ: 4回目以降、現時点の取り組み 上記を踏まえて改善していきます。なお 1回目からモブワーク後にすぐ感想・意見を募りました(今も終わったらすぐ振り返ります)。高速で回して高速でフィードバックし高速で次に反映させます 。こういう試しながらやるスタイルはダラダラやるとメリットがわからず頓挫するので、高速で良くしていく方法が良いです。 What: やること 画面への動線追加 Cookie を使った振る舞い追加 レコメンド機能実装のためのサードパーティスクリプト組み込み 前回よりバラエティにとんだタスクで多種多様 Who: 3,4人 When: 3時間 25分, 5分休憩のポモドーロテクニックを利用 集中していようが絶対に区切る その都度ドライバーを交代する Where: 疲れないフリースペース How: 基本的に全員個人の業務端末は閉じる、ドライバーだけが端末を触っている状態 初回からどう変わったか 改善によって得られたものは大きかったのですが、 これはあくまで私たちのチームの所感によるところが大きいです 。前回から比較して課題と感じたものがどうなったかといいますと、 「終わった後に進捗や成果が気になってしまう」 極力気にしなくなりました。こういうものだろう、と。 人数が9人から3,4人に少なくなったことで心理的負担が減った のもあります。チームで集まってからタスクを洗い出しして取り掛かる、といった具合で進めており、これは回数を重ねるごとにメンバーでやりきれる範囲が見えてきたり見積もりの精度も上がるのだろうなと思っています。 「首が痛い」 フリースペースに大きめのディスプレイをおいて全員がそれを正面から見れる環境を重視しました。 モブする環境が本当にめちゃくちゃ大事です 。あと休憩中に立ちあがって伸びをする、立ち話しをするなど身体を恣意的に動かしたりしています。9人からモブするチームを分けたのもあり、別のチームのモブの様子を覗きに行ったりもしました。 「わからないことを質問していいのか迷う」 こちらについても人数を減らすことが功を奏して自然な会話の中で誰も話さない、ということはないように感じます。なお、出入り自由としてあまり今活躍の場はなさそうだ・必要に応じて参加するね、と感じたらすぐ離れて良いとしています。 「ドライバーの役割問題」 一旦ルールは忘れてやりやすいようにやってみました。ドライバーも自ら書けるものは書きますし、変数の宣言でわざわざ誰かの “c o n s t” という言葉を待ってる必要はありません。変数名はどうするの? キャメルケースだよね、など 質問しつつ会話しながらそれをコード化する役割がドライバー という感じです。 初回からさらによくなっていること 「モブ中に Slack やよそ見をしていない」 集中力が圧倒的に違います。 ドライバー以外は意図的に端末を閉じた状態で(むしろ持ってこないでよい)全員ディスプレイを見て同じ作業に没頭するのでモブが終わるとぐったりするほど集中しています。 なので、ペース配分なども考えていった方がいいのだろうなと感じています。 “一人で作業しているのとは別のエネルギーを使っている気がする” という感想があり、もしかしたら一人でコードを書く際には使わない会話のエネルギーも使ってるので、総合すると一人より疲れが大きいように感じます。 1日のモブでコミット・プルリクエスト作成(実際には合意を得ているのでレビューしませんが)まで行くと達成感・満足感が高いです。 「何でも聞く・話す」 とにかく質問する・作業中に話す習慣がついているように感じます。この粒度だったら新しいクラス作って import する? この className を付ける時ってマルチクラスになるけどどのセレクタ最初に書いてるの? みたいな話が自然と流れてきます。 これってプルリクエストで指摘されると面倒くさいやつですよね。タイムラグがあるため、プルリクエストが上がってレビュアが指摘コメントをした後にレビュイーの fix commit までおそらく長いと3日くらいかかりそうです。 メンバーの合意がその場で取れてコードレビューが不要となるのは時間が相当短縮されているのではないでしょうか 。 「途中の休憩がかなり息抜きになる」 5分の休憩時に、海外ドラマの話になってモブと同じくブラウザで調べてみんなで情報を見たり、お菓子食べながら談笑したり、 モブ中の休憩もほとんどモブしているような状態がかなり良い です。メリハリはもちろんつけるべきで、タイマーをつけてアラームでしっかり動けるような動きもきちんとしてくと良さそうです。 「これが暗黙知の共有、という気付き」 属人化したタスクによって引き継ぎされないケースだとか、ドキュメントがどこにあるかわからず着手できないケース、といったような事態の歯止めに成り得るなとモブをするたびに感じます。上記でも触れたようなプルリクエスト上で指摘されがちな「自然とこうなっているコードスタイル」はじめ、 属人化しているタスク・ルールってどのプロジェクトにもあると思ってまして、暗黙知とされているものが多ければ多いほど人から引き剥がすのに有効かなと感じます 。 今後は監視ツールの見方や使い方、一人しか触らない可能性のある CI の設定・YAML、引き剥がせるものからどんどんモブでこなしていきたいと感じています。 そして何より及部さんの資料にある通り、コミュニケーションの問題を一気に片付けられることが大きな利点ではないかなと感じています。 まとめ 雑にまとめてしまうと、 人には人のモブワーク 、なんですが雑すぎるので私が感じているモブプログラミングの今のところのベターな方法としては、 まずは短時間で、細かい集中時間で、小さなチームで リラックスできる体勢を保てる環境で ドライバー以外は端末を開かない そして 一番大事なのはメンバーを思いやる気持ちや敬意、より良い場にしようというチームの意識 なのかなと。 本日は以上です。
アバター
こんにちは。auパートナー本部の苅部です。 Google Discoverの記事レコメンド経由(googleapi.com or discover.google)のアクセス増の話題が最近増えているので、ログの残り方とアクセス規模(ニュースサイト)を簡単に調査してみました。 Google Discoverとは GoogleChrome(新規タブ)やGoogleアプリで表示されるニュース記事のレコメンドで、機械学習を用いてユーザーの行動データから最適なコンテンツが選別されています。 Android7あたりからはホーム画面をスワイプするだけで表示させる事ができます。 リクエストヘッダーの確認 アクセス解析として個々の流入元ごとにセグメントを作りたいので、パケットキャプチャでそれぞれのリクエストヘッダーを確認しました。 1. Googleアプリ レコメンド箇所のリクエストヘッダーは以下の通りです。 Android 4 key value Referrer android-app://com.google.android.googlequicksearchbox/ UserAgent Mozilla/5.0 (Linux; Android 4.4.2; SOL23) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.28 Mobile Safari/537.36 Referrerとしてandroid-appから始まる独自URIが渡されています。 iOS 10 key value Referrer なし UserAgent Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) GSA /65.0.225212226 Mobile/14F89 Safari/602.1 Referrerは渡されていませんが、代わりにUserAgentでGSAという文字列が確認できました。 タグマネージャーあたりで文字列を引っ掛けてカスタムディメンションにセットすればGSAWebviewとしてのセグメントが切れるようになります。 ※GSAWebviewでの自然検索遷移では、Referrerが[google.com]として渡されていました。 2. Google Chrome (v71) 新規タブを開いた時のレコメンド箇所のリクエストヘッダーは以下の通りです。 Android 4 key value Referrer https ://www.googleapis.com/auth/chrome-content-suggestions UserAgent Mozilla/5.0 (Linux; Android 4.4.2; SOL23) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36 ※Android8でも確認しましたが、Android4同様のReferrerでした。 iOS 10 key value Referrer https ://www.googleapis.com/auth/chrome-content-suggestions UserAgent Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) CriOS/71.0.3578.89 Mobile/14F89 Safari/602.1 AndroidOS,iOSともに[https://www.googleapis.com]から始まるURLがReferrerとして渡されていました。 というわけで、セグメント化の可否をまとめるとこんな感じになります。 Googleアプリ レコメンド GoogleChrome レコメンド Android ○ (Referrer) ○ (Referrer) iOS △ (UserAgent) ※ ○ (Referrer) ※ [GSA文字列特定]と[directセッション]のAND条件で推定できると思います。 セグメント作成 リファラーの文字列をGoogleTagManager経由でGoogleAnalyticsのカスタムディメンションにセットしているので、当該ディメンションで文字列を指定することでセグメントを作成が可能になります。 1. Googleアプリレコメンド(Android) ※iOSのトラッキングは今回間に合わなかったので、確認でき次第追記したいと思います。 2. Google Chromeレコメンド 3. discover.google.com経由(流入元不明) トラフィック確認 今回は弊社で運営しているニュースメディアの1サイトで、トラフィックの規模を確認しました。※ニュースサイト以外ではトラフィックが確認できませんでした。 ・参照元/メディア googleapis.comが6位、discover.google.comが14位に入っています。 ・時系列推移 2018年4月〜12月までのセグメントごとのセッション数の推移です。 1. Googleアプリレコメンド アクセスは4月から確認できているものの、8月後半から大幅に上昇しています。 2. Google Chromeレコメンド 10月中旬に急落していますが、11月末から急上昇しています。 3. discover.google.com 10月中旬に突然現れて、12月に下落しています。 ※1サイトのアクセスログですので、今回の調査からGoogleのアルゴリズムや実装の変化を推測するのは困難です。 ・MAU全体に占める割合 以下の設定でレコメンドトラフィックのセグメントを作り全体における割合を測定しました。 結果としては MAUの15%ほどがレコメンドトラフィックによるもの と推定できました。 レコメンド経由は新規ユーザーの割合が多いため、MAUに貢献できたようです。 ・OSごとの違い 「Googleの検索ウィジェット(QSB)での自然検索遷移でもandroid-appから始まる独自URIがリファラーで渡る」という情報もあり、アプリのバージョン or OSのバージョンによってリファラー仕様が異なる可能性もあります。 そのため、念のため複数のOSでも比較しました。 OS ver レコメンドの割合※ Android 8 17% Android 7 16% Android 6 14% Android 5 9% Android 4 5% ※当該OS MAUにおける当該OSレコメンドMAUの割合 この比較ではOSのバージョンが上がるごとにレコメンドの割合が増えている事が分かります。 実際にOSのバージョンが上がるごとにホーム画面からの検索アクセスが容易になっているため、MAUの15%程度という今回の推測はある程度妥当かと思いました。 おわりに SEOの辻さんが仰っているように Direct扱いのGoogleアプリ流入(iOS)があるはずなので、今回の数値を考慮するとレコメンド流入は無視できない規模になっているのではないかと思います。 google.comでも近いうちにDiscoverが実装されるようなので、これからすべてのクライアントでレコメンドトラフィックが増加傾向になると思われます。 今後も音声検索も含め、調査を継続して進めていきたいと思います。 参考記事 2019年注目のサービス「Google Discover」 仕組み、SEOへの影響、最適化手法 Discover new information and inspiration with Search, no query required Google Discover - Search Console ヘルプ
アバター
この記事は、 mediba Advent Calendar 2018 の20日目です。 こんにちは。コミュニケーションデザイン本部 創造部の森竹です。 バックエンド開発を担当しています。最近は カイゼン・ジャーニー を通じてのアジャイル/スクラムの推進や BIT VALLEY -INSIDE- のコミュニティ運営に参画しています。 今回は先日の BIT VALLEY -INSIDE- Vol.2 でライトニングトーク(LT)させて頂いた内容を中心に記事とさせて頂きました。あるプロダクトのバッチアプリケーションアーキテクチャのジャーニーを紹介します。 AWS Batch AWS 環境でのバッチと言えば、 AWS Batch ではないでしょうか。 AWS Batch の特徴は下記の通りです。 フルマネージド型です。 AWS Batch の実体は Amazon ECS 、またその実体は EC2 の構成です。スポットインスタンスへ入札することが出来ます。 2018年3月 に CloudWatch Event に対応し、 cron 的な使い方が出来るようになりました。 Docker コンテナ🐳でアプリケーションを実行します。 ログは CloudWatch Logs に出力します。 実行パターンの1つに、「 fetch-and-run 」があります。 fetch-and-run AWS から提供されている Dockerfile 、 fetch_and_run.sh を使用し、下記の流れで実行します。 AWS Batch が Docker コンテナとしてジョブを実行する。 fetch_and_run.sh が実行され、 AWS S3 からアプリケーションを取得する。 アプリケーションを実行する。 アプリケーションを AWS S3 へ配置するだけで、バッチアプリケーションが実行可能となります。エンジニアはアプリケーションの開発に注力することが出来ます。 ※LT時には fetch-and-run で引数を扱えないとお話ししてしまいましたが、引数を扱うことが出来ました。大変失礼しました。 アプリケーション開発言語 今回は Go 言語を採用しました。 社内では2016年頃から使い始め、実績があります。バージョンは最新バージョンの Go 1.11 です。 Go言語のシングルバイナリは扱いやすく、 fetch-and-run との相性は良さそうです❤️ ビルド/デプロイ ビルド Travis CI にて Go ビルドを実行し、バイナリを AWS S3 (ビルド用バケット) へPUTします。 デプロイ 環境毎に作成した下記ブランチへのマージをトリガーに、 Travis CI にて AWS S3 (アプリケーション用バケット) へPUTします。 fetch-and-run でのデプロイは、これだけです❤️ deployment/production deployment/staging deployment/development Fargate 2018年8月に「タスクのスケジュール」に対応したと 発表 がありました。 AWS Batch でやろうとしていた事と同じことがクラスター管理( EC2 )不要で実現出来るようになりました❗️ まとめ バッチのアーキテクチャジャーニーを紹介させて頂きました。 今回は AWS Batch ではなく、 AWS Fargate を採用し、 Go のバッチアプリケーションを fetch-and-run を使って実行するアーキテクチャとしました。もちろんバッチアプリケーションの仕様や特性により、 AWS Batch を採用することもあると思います。 CI には Travis CI を使用していますが、 AWS CodePipeLine/CodeBuild でも良さそうです。 今後は EC2 で実行している既存のバッチアプリケーションを AWS Batch 、または AWS Fargate へ移行することを検討して行きたいと思っています。 参考 Creating a Simple “Fetch & Run” AWS Batch Job AWS Batchでシェルスクリプトを実行する典型的パターンのご紹介
アバター
この記事は、 mediba Advent Calendar 2018 の10日目です。 コミュニケーションデザイン本部 創造部 アプリ開発グループの松島です。Xamarinが大好きです。 今回は、 Xamarin.Forms 4.0 のプレビュー版がリリースされたので、早速試してみました。 試したバージョンは4.0.0.8055-pre1です。 新機能 以下の3つの機能が追加されました。 Shell Visual CollectionView 現状では、これらの機能を使うためには、 AppDelegate や MainActivity で以下のように設定する必要があります。 global::Xamarin.Forms.Forms.SetFlags("Shell_Experimental", "Visual_Experimental", "CollectionView_Experimental"); Shell Shellは、 MasterDetailPage のような所謂ハンバーガーメニューを作成する機能です。 MasterDetailPage とどこが違うのかというと、 MasterDetailPage でページを切り替えるには、前のページを破棄して、切り替えるたびに新しくページを作成することになるのですが、 Shell は、最初にページを作ってしまい、あとは表示を切り替えるだけになっています。その点は、 TabbedPage に似ています。 Shellは、以下のように使います。 <Shell xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamarinForms4App" x:Class="XamarinForms4App.MainPage"> <Shell.Resources> ... </Shell.Resources> <ShellItem Title="Visual"> <ShellSection Title="Material"> <ShellContent> <local:MaterialPage/> </ShellContent> </ShellSection> <ShellSection Title="Normal"> <ShellContent> <local:NormalPage/> </ShellContent> </ShellSection> </ShellItem> <ShellItem Title="CollectionView"> <ShellContent> <local:CollectionViewPage/> </ShellContent> </ShellItem> </Shell> ShellItem で、メニューを追加して、 ShellContent に表示するページを含めます。 ShellSection を使えば、タブで切り替えることができるページにすることができます。 あと、iOSでメニューアイコンを表示するには、 3bar.png という名前で画像を含めないといけないようです。 Visual 今までは、共通に書いていても、iOSとAndroidで見た目が結構異なり、デザインに苦労することがありました。4.0からは、マテリアルデザインで見た目も同じようにすることが可能になりました。 Visual プロパティを Material にすることで、マテリアルデザインにすることができます。 <ContentPage ... Visual="Material"> ... </ContentPage> CollectionView グリッド表示をするには、 AiForms.CollectionView というライブラリを使っていたのですが、ようやく公式にも登場しました。 カラム数が3つのグリッドを作るには以下のようにします。 <CollectionView ItemsSource="{Binding Items}" > <CollectionView.ItemsLayout> <GridItemsLayout Orientation="Vertical" Span="3" /> </CollectionView.ItemsLayout> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout HeightRequest="100"> <Label Text="{Binding Title}"/> <Label Text="{Binding Detail}" FontSize="Small" /> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> 現状では、クリックイベントもスペースの設定もできないようなので、まだまだこれからといった感じです。 GitHub 作ったものは ここ に置いておきます。 おわりに 最近、Xamarin.Formsの進化が目覚ましいです。まだまだのところもありますが、大分使いやすいものになってきました。今後も要チェックですね。
アバター
この記事は mediba Advent Calendar 2018 4日目の記事です。 おはようございます。 こんにちは。 こんばんわ。 創造部 新米部長 尾野です。 弊社の社員が運営メンバーとして参画している、 「BIT VALLEY -INSIDE-」というコミュニティにてお話させてもらった内容を中心に書き記します。 その時の模様は @samuraiRed さんにブログにしていただきました。 嬉しい限りです。 https://blog.samuraikatamaris.red/entry/20181025/1540437510 すごく良いコミュニティなので、よかったらイベントに参加してみてください。 Facebookページ さて本題です。 きっとどのIT企業に於いても技術的負債ってあると思います。   無論、弊社にもあり〼   私が担当しているPJに於いても、大きな技術的負債があって、 単純に時間を費やして負債をゼロにするだけじゃモッタイナイと思い・・・   そんなお話です。 前提 PJ内のエンジニアの割合(2018/03当時) フロントエンド:4人(クライアントサイド) サーバーサイド:8人(PHP) きっかけ とはいえ技術負債駆動刷新じゃないんです。   PJチーム全体でのエンジニアリングチームの稼働バランスを見た時に、 フロントエンド:残業過多 サーバーサイド:ホワイト企業 のような状況でした。   UX/UIを起点に要件が発生するのは当然ですよね。 視認性を持つUIに対してユーザーは反応しますし、そこに対してカイゼンってなりますわそりゃ。   昨今、Webブラウザで出来る事はものすごく増えました。   Server Side Renderingに割く処理コストって、もっとWebブラウザに寄せたほうが体験も良くなるし、手元の端末に処理を寄せればサーバーリソース/コストも減らせるよね、と。     もっと手元に処理を寄せよう。 もっとユーザー接点を司るエンジニアを増やして、総体的にエンジニアとユーザーの距離を縮めよう。     そう、 ブラウザが主戦場 なんです by 社内のエンジニア     役割定義 ということで役割定義/技術スタックを大きく変えました。 Universal JavaScript化 =サーバーサイド/フロントの人員リソースの効率化   SPA/遷移先ページのprefetch/dynamic import/最適なSSR等による画面描画と遷移の体験を改善 =UX向上 今までありがとうPHP。僕はキミ(PHP)に多くの事を学びました。   サヨナラPHP 僕は今TypeScriptに夢中です。   そして、1つのチームに責務を持ってもらって、チューニング出来た方が、 コミュニケーションコストも減らせるし、責務に集中出来るから良いよね   こんなイメージで分けました。 サーバーサイドの部分をWeb+Rest API(今回はGraphQLにしましたが)/Consoleに分離して、フロントとバックエンドに寄せて2層にしました。 責任分界と超概要構成   こんなイメージ   あ、そうです バックエンドのバッチもPHPは辞めてGolangにしました。 知見は既にあったので概ね無問題でした。   責務範囲は フロントエンド:ブラウザ+サーバーWeb+API(Backends For Frontends) バックエンド:バッチとインフラ   とし、責任分界のIFはフロント側で設計する様にしました。 何かしらのstorageのschema依存ではなく、利用する側が利用意図に即した形で設計し、Provider側はデータ提供の為に作り込む、が正義だと (Consumer Driven Contract的な) 学習コスト 個人)ひたすらコード書きました。    ↑↓ 先陣チーム)実装基板を作る    ↑↓ PJチーム)モックを作る所からはじめ、定期的なティーチングを繰り返す   というのを繰り返しながら浸透を図りました。 開発Phaseにて PJの途中(中盤〜後期)からではありますが、 eXtream Programming的なアプローチも取り入れる事にしました。   どうしても開発Phaseにおいてドライブ掛かるとPull Requestって溜まっちゃうんですよね。レビュー後回し 定期的に固定のレビュー時間を取っても進捗しないんですわやっぱり。   そして、属人的にコード書いちゃうままレビュー出来ずに問題を顕在化出来ない→品質が向上しないという負のスパイラルに陥ってしまうので・・・ 3人1組にしてライブコーディングをする事にしました。   モブでもペアでもなく、ナビゲータ/ドライバで分けることもなく、スタック分界で接合点を話しながら進めていくスタイルを取りました。   こんなルールでやってます。 リーダー的な人がブランチ切る セッション張る みんながそこに参加 みんなでTODOリスト作る 一人でやりきれるボリューム感 TODOリストをタイムボックス管理する 例)1時間と決めて消化を確認する VSCodeのLive Shareという機能、とても良いみたいです。 時間都合が合わせられない僕は参加出来てませんorz とても楽しそうだし「効率あがった」という意見もいただきました。 羨ましいorz 今 これから試験Phaseに入っていきますが、ここも責務分離でワッショイします。 非機能班 パフォチュー+リファクタ 全体で言うとボリュームテストと例外テストもする 機能班 バグチケ+E2E+リファクタ みたいなイメージで進めようと考えてます。雑ですが明かせる範囲はココマデ 2019のいつか、きっと素敵な使い心地をまとったプロダクトが世の中に放出されます。 未来のお話 プロダクトを長い目で見た時にグロースしていく時期のほうが圧倒的に長いと思います(ケースバイケースですが) バイモーダルITにおけるSoR/SoEのシフトチェンジってとても重要だなーと  その上で、グロースPhaseにおいてはSoEにギアを入れ替え、しっかりとユーザーの反応を見ながら、ユーザーに求められる方向にプロダクトを成長させていけるプロセス/技術に変えていこうと考えてます。 技術は事業成長のために使うもんだと。 まだ言えませんが、まさにこれから大きな新サービスをMicroservicesで仕立てていく計画があったりなかったりします。 まだまだ挑戦しがいのあるサービス構想が沢山ある中で、ユーザー価値を中心に据えてモノづくりをしていける文化をこれから創っていこうとしてます。 そんな文化創りから携わりたいやっていき力の強いエンジニアを求めております。   では
アバター
この記事は mediba Advent Calendar 2018 二日目の記事です 出展 : https://dic.nicovideo.jp/a/%E3%81%82…%E3%81%82%E3%82%8A%E3%81%AE%E3%81%BE%E3%81%BE%20%E4%BB%8A%20%E8%B5%B7%E3%81%93%E3%81%A3%E3%81%9F%E4%BA%8B%E3%82%92%E8%A9%B1%E3%81%99%E3%81%9C%21 いいのかのっけからこんなふざけてて…. 自己紹介 メリークリスマスまで 3 週間ちょいです、いかがお過ごしでしょうか コミュニケーションデザイン本部 創造部 アプリ開発グループの 佐藤禎章と申します 本業としては、Android/iOS のネイティブアプリの構築をするメンバーのお手伝いをやっています 今回作ったもの 今回出来ていたものは、 JPG 画像の圧縮率を比較できる SPA です https://github.com/medi-y-sato/imageComplesser now に publish しておきました https://build-fbillkollb.now.sh/ 中身の解説 画像を指定すると、JPG でクオリティ 10%刻み 10 段階の画像を生成し、リストします AngularDart のはじめかた 作るときのざっとした流れだけ、記載しておきます Dart for Web の Get Started の 2 と 3 を実施して、Dart の SDK と webdev 、 stagehand を導入しておきます プロジェクト用のディレクトリを作成後、そのディレクトリ上で stagehand web-angular とし、初期テンプレを展開してもらいます その後 pub get を実行すると、カレントにある pubspec.yaml を参照してパッケージを導入してくれます webdev serve とすると開発モードでローカルにサーバが立ち上がるので、出力にある URL(大概 http://localhost:8080 だと思います)を開いて確認してみてください webdev build とするとカレントの ./build/ ディレクトリにビルド結果が出力されるので、適当な所に設置してください AngularDart で取り扱うコードは ./lib/ 以下にありますので、 ./lib/src/app_component.dart とかテンプレコンポーネントとかを眺めて構成を確認すれば、Angulra いじったことがある人なら「あーなるほど」と思うでしょう 今回やったこと Dart っぽい事はあんまりやっていません TypeScript 書いてた人なら Visual Studio Code のサジェストとカンで対応させられるレベルなので、Angular2 以降を触ったことがある人なら AngularDart はそう難しくないと思います base64 を取り扱うための crypto 、画像処理を行うための image パッケージを pub から導入 dependencies: crypto: any image: any ファイルのアップロードみたいな UI を作成 ファイルの選択が完了したらすぐにコンバート処理を走らせたかったので、 (change) でいきなりメソッド呼び出す行儀の悪い仕掛けにしてます <input style="display: none" type="file" accept="image/png"><button>Select File</button> 画像は全部 Base64 でハメコミ img の DOM にバイナリとして放り込むのが正しいのかもしれませんが、めんどくさかったので以前から知ってる方法で target.src = 'data:image/jpeg;base64,${Encoded64}'; なんでこんなものが出来たの? 別件でファイル加工処理のバッチを Perl で作っていた でも今どきバッチ処理程度で Perl 環境インストールしてもらうのも大変だよなあ、モジュールとか面倒だし、と Perl を思い直した そこで node/typescript で書いてみたが、割とファイルの取扱がめんどくさかった そんな折に Dart2 と Flutter のニュースを耳にした 調べてみたら pub というパッケージ管理の仕組みが(-npm の再発明かと思ったけど-)ちゃんとしてて、Web サーバ側やローカルでの処理もちゃんと書けそうに見えた ので、ファイル加工のバッチを Dart2 で書いたら、すごいあっさりさっぱり書けてびっくりした ※ このバッチ自体はちょっと機密なものなので公開できないです、ごめんなさい ※ やってることはファイル読んで RegExp の match や replace ゴリゴリする、よくある奴です で、このお手軽さを誰かに伝えたい、と思い、 特定ディレクトリにある画像全部を PNG の圧縮レベル最大でコンバートし直したらどれだけ小さくなるかツール を作ってみたのですね そしたら全然変わんなかったんです、みんなちゃんと最大レベルで圧縮してやんの なんか悔しくなったので、画像ごとに jpg の圧縮レベルを変えて画像を沢山生成してみたら、意外と面白い結果になったのですね 自然画だったらクオリティ 50%くらいに落としても結構違いが分からないんだなー、とか、その割に容量あんまり変わらないんだなー、とか で、ファイル生成自体は簡単に出来るんですけど、並べて比べるのが面倒だったんですね Finder とか Explorer でサムネイル表示にして見比べて、とかやる必要があったので だったらこの比較をする Web ページを生成しちゃえばいいじゃん、と思いまして、作り始めました それでページ生成方法を考えたのですが、真面目に HTML 吐き出すよりはフレームワーク使ったほうが楽だよなー、と思いましてちょっと調べたら、あるじゃないですか AngularDart ってのが 以前 ionic を扱っていた こともあって Angular は分かっているので、昔取った杵柄とばかりにさくっと表示させてみたら、さくっと出来ました それが、この成果物です ….で、ここまで来て気づいたんです Dart2 の CLI ツール作るんじゃなかったんかい すみません他にもいろいろ考えたんですけどどうしても面白い CLI ツールにならなかったのです、ごめんなさい ※ Dart CLI で Google Analytics のデータ引っ張り出して加工して Slack に投げる、とかやればよかったのかな…. あとがき Dart の強力なところは サーバサイドもクライアントサイドも同じ気持ちで書ける という、JavaScript と node が持っていた特徴をそのまま引き継ぎ、 モバイルアプリも同じ気持ちで書ける という特徴を Flutter で足してある所です CI ツールから Web アプリにさらっと転向して、出来ちゃう という辺りから、そのお手軽さを感じていただけたとしたら、コレ書いた甲斐があったというものです 加えて言語仕様として型の取り扱いを頑張っているので、TypeScript で感じた安心感を、Dart でも感じています はじめから async/await や stream などもありますし、大体のものはもう書けちゃうでしょう Dart、結構未来、あるかもしれません というわけで、mediba では言語やフレームワークにとらわれずに 何を作るか で考えられるエンジニアを募集しています 特に ネイティブアプリのエンジニア をですね….
アバター
※これ↑、Systems Manager のアイコンらしいです。 おはこんばんちは、インフラストラクチャー部の沼沢です。 みなさん、AWS Systems Manager 使ってますか? Systems Manager を使うことで、大量のサーバーの運用保守作業を自動化したり、ソフトウェアインベントリを収集して一元的に確認したりすることができるので大変便利ですよね。 AWS Systems Manager – 運用のインサイトを入手して迅速に対応 そんな Systems Manager に先日、 セッションマネージャー という機能が登場しました。 最新 – AWS Systems Manager セッションマネージャーで EC2 インスタンスへのシェルアクセスを実現 | Amazon Web Services ブログ SSH不要時代がくるか!?AWS Systems Manager セッションマネージャーがリリースされました! | DevelopersIO で、これの良いところは コンソールからシェルアクセス可能 EC2 インスタンスに対して SSH の穴あけ不要 誰がセッション開始したか、履歴が残る 操作ログを S3 や CloudWatch Logs に出力できる だと思っています。 正直、IAM ユーザーをしっかりと個人ごとに発行していれば、OS アカウントを個人ごとに作成する必要が無くなって良いなーと思ったので、あとは操作ログの出力を強制できれば監査にも使えるなーと思い、調べてみました。 実現したいこと Systems Manager 自体は必要な人は誰でも利用できるようにしたい マネージドポリシー AmazonSSMFullAccess を付与するイメージ セッションマネージャーの設定は、特定の人以外は操作できないようにしたい 設定の変更 ≒ ログ出力設定の変更 これを実現する IAM ポリシーが無いか、調査・検証してみました。 調査結果 兎にも角にもまずは公式ドキュメントをチェックしました。 そしてしっかりと答えが書いてありました。ビバ公式ドキュメント。 ただし、本稿執筆時点(2018年10月)では日本語ドキュメントにはまだ無いページなので、日本語ドキュメントで探していると出てこないので注意が必要です。 Grant or Deny a User Permissions to Update Session Manager Preferences - AWS Systems Manager User policy to prevent preferences from being updated にはこう書いてあります。 { "Version": "2012-10-17", "Statement": [ { "Action": [ "ssm:CreateDocument", "ssm:UpdateDocument", "ssm:DeleteDocument" ], "Effect": "Deny", "Resource": [ "arn:aws:ssm:us-east-2:123456789012:document/SSM-SessionManagerRunShell" ] } ] } この IAM ポリシーで実現できるようです。 ただし、Resource でリージョン us-east-2 と AWS アカウント ID 123456789012 が指定されているので、ここは面倒なのでワイルドカードにしてしまいましょう。 特定のリージョンだけは許可したい、という要件が無いのであれば、ワイルドカードにしても特に問題は無いと思います。 するとこうなります。 { "Version": "2012-10-17", "Statement": [ { "Action": [ "ssm:CreateDocument", "ssm:UpdateDocument", "ssm:DeleteDocument" ], "Effect": "Deny", "Resource": [ "arn:aws:ssm:*:*:document/SSM-SessionManagerRunShell" ] } ] } マネージドポリシー AmazonSSMFullAccess を付与したユーザーに、このポリシーを追加で付与し、セッションマネージャーの設定変更を試してみました。 試してみた まとめ ログ出力設定を変更できないようにすることができました。 冒頭でも述べたとおり、セッションマネージャーには以下の利点があると考えています。 コンソールからシェルアクセス可能 EC2 インスタンスに対して SSH の穴あけ不要 誰がセッション開始したか、履歴が残る 操作ログを S3 や CloudWatch Logs に出力できる 今回のログ出力設定の変更を拒否するポリシーを適用した上でセッションマネージャーを導入することで、IAM ユーザーさえしっかり管理できていれば、 個人 OS アカウント不要 = IAM ユーザーと OS アカウントの二重管理が無くなる 個人 OS アカウント不要なので、秘密鍵/公開鍵も不要 OS 操作ログを永続化する仕組みを自前で用意、運用しなくて良い 運用負荷軽減! になると考えていますので、今後活用していきたいと思います! 補足 もちろん本格的に導入の際は、S3 に吐かれたログファイルや CloudWatch Logs のストリームなどの操作を制限する IAM ポリシーも必要なのでお忘れなく!
アバター
こんにちは。フロントエンジニアの苅部です。 今回はECサイトでのレコメンドエンジン評価にあたって、Google Optimize360のフロント組み込みやGoogle Analyticsのユーザーリスト利用を実施しましたので一連の流れを共有したいと思います。 テスト実装であったりレコメンド評価の理解の一助となれば幸いです。 ※Google Optimizeの導入方法については過去の記事をご確認ください。 ・ Google Optimize導入とA/Bテスト実施のポイント | mediba Creator × Engineer Blog レコメンドエンジン評価の目的 「どのくらいの効果が見込まれるか」「A or B or n どのモデルが優れているか」といった疑問を明らかにするためにレコメンドエンジンのパフォーマンス評価を実施しました。 「効果」とは費用対効果であったり事業指標への貢献度合いとなります。 インフラや保守など何かしらの形でアプリケーションを維持するためのコストは発生するため、効果とコストのバランスが取れているか評価することが大切です。 またレコメンドエンジンは開発して終わりではなく、パフォーマンスを計測して評価・改善を続けることが望ましいです。 ただ前後比較の評価では他の要因を除去できず因果関係の判断が難しいため、より正確なモデル評価のためにA/Bテスト(統計的な因果推論)の必要があると考えています。 今回のレコメンド実装方法 レコメンドの有無でのA/Bテストを前提に、以下のような形で実装を進めました。 ・API 分析会社様に構築いただいたレコメンドのAPIを使い、AJAXにてレコメンド結果をJSONで受け取っています。 アクセスログ・コンバージョンログを元にバッチ処理でアソシエーション分析を行い、ユーザー・商品ごとに最適な(購買される可能性の高い)商品を提案しています。 ・DOM Google Optimizeのエディタで空タグの挿入とJavaScriptの実行を指定しています。 そして別JavaScriptファイルにて[AJAX実行,HTML文字列構築,引数のHTMLElementへinnerHTML代入する関数]をグローバル空間に用意して、それをGoogle Optimizeから呼び出せるようにしています。 ・任意の箇所へ空タグを挿入 ・空タグの中でJS(AJAX/innerHTML)を実行 Google Optimize側のエディタを使ってDOM書き換えやJavaScript(AJAX)の実行も可能ですが、今回はターゲティングルール設定・関数実行・テストの評価をGoogle Optimizeに委ねて、ロジック・ビューは別JavaScriptファイルに持たせています。 メンテナンス性やコードレビューの観点で、可能な限りコードとしてGitでバージョン管理する方が好ましいと考えています。 ※この方法を取るとテスト期間中にもDOM変更が可能になるため、何かと便利です。 ※クリックイベントも併せて取っておくと後の分析で役に立ちます。 A/Bテストの実施概要 今回は複数画面でのレコメンド実装を想定しています。 ただGoogle Optimizeは基本的に1画面1テストとなるためこのままではページごとにA/Bテストの抽選が実施され、それぞれでテストパターンが変化してしまいます。 レコメンド有無やモデルの比較のために「レコメンドのある世界・レコメンドのない世界」「モデルAの世界・モデルBの世界」 を作りたいわけですが、このままではそれぞれの世界が混在して本来意図していたテストが実施できません。 そこで今回は、Google Optimize360(有償)の機能を利用してGoogle Analytics側のユーザーリストをインポートしてターゲティング設定することにしました。 ちょっと複雑ですが、テスト動作の全体の流れは以下の通りです。 起点ページにてユーザーごとにテストパターンが決定 Google Analytics側にシステムディメンションがセット(ID/パターン) ユーザーリストが動的に生成(時差有り) 他ページでもユーザーリストのターゲットに対してテストが着火 ユーザーリストはリターゲティング向けの機能となりますが、Google Optimize360ではA/Bテストのターゲティング配信の条件としてGoogle Analyticsのユーザーリストをインポートすることができます。 Google Optimize,Google Analyticsの設定 1. テストの起点となるページのテストIDを取得 まずはテストパターンの抽選が実施される起点ページを決め、Google Optimizeでテストを作成します。 下書きの状態でテストIDが割り振られるため、このテストIDを他のページでのターゲティング配信に利用します。 2. テストIDを元にセグメントを作成・ユーザーリストを公開 起点ページで実施するテストのテストIDとテストバリエーションを元に、Google Analytics側でユーザーリストを作成して公開します。 まずはセグメントビルダーにてGoogle Optimize側で発行されたテストIDを利用して任意のテストパターンを指定します。 ・[テストID]ディメンションと[パターン]ディメンションを使う場合 ・[パターンを含むテストID]ディメンションを使う場合 パターンディメンションは、オリジナルであれば[0]、バリエーションの1つめであれば[1]、2つめであれば[2]といった形で連番で指定する事ができます。 今回はA/Bテストのバリエーションの1つめを判定したいため[1]としています。 これで[A/BテストのBパターンが表示された人(レコメンドが表示された人)]というセグメントが作成できました。 次に、セグメントを元にユーザーリストを作成します。 ・一覧画面にて任意のセグメントで[ユーザーリストを作成]を選択 ・ユーザーリスト名を入力 ・ユーザーリストの宛先にGoogleオプティマイズ360を選択 ・公開先にGoogleオプティマイズ360が含まれていることを確認して公開 以上でユーザーリストの設定は完了です。 今回の仕組みでは全ページのテストを稼働させるためにユーザーリストが更新される必要がありますので、数時間の開始時差を許容する必要があります。 なお今回は最短で4時間程度でユーザーリストが反映されました。 3. 画面ごとにテストを作成 レコメンドを掲出する画面ごとにテストを作成します。 ・ターゲティング条件に任意のユーザーリストを指定 ・テスト目標は収益とトランザクション数を指定 一連の設定が完了したので、すべての画面でテストを開始できる状態となりました。 A/Bテストを始める前に A/Bテストは万能ではなく、実施においてはサンプルの[偏り]を常に意識することが大事です。単純無作為(ランダム)に割り振っても必ず偏りが発生しますし、サンプルが少ないほど偏りやすくなります。 特に リピーターの比率が高いサイトでは、偏った場合にはそのテストの中では偏り続ける ことになります。 そのためA/Bテストの実施にあたっては[事前にA/Aテストを実施して偏りの起こりやすさを把握]したり[A/Bテスト期間だけでなく、実施前期間まで含めた前後比較で各バリエーションの評価すること]が大事です。 ・直帰率でのA/Aテストの例。徐々に偏りが減少していきます。 レコメンドエンジンの評価結果 さて、1ヶ月ほどレコメンドの有無でA/Bテストした結果は以下のようになりました。 ・Google Optimize レコメンド有りのパターンの売り上げが[95%の確率で4%〜16%、中央値で10%の向上]という、なかなか良い結果となりました。 95%信頼区間にマイナスの値を含んでいないため、今回は[レコメンドによって収益の向上が期待できる]という判断をしました。 ・Google Analytics レコメンド有り・無しのセグメントを作成し、eコマースレポートにて重ねて比較してみました。ブルーの線がレコメンドなしのセグメントで、オレンジの線がレコメンドありのセグメントです。 ※ユーザーセグメントであれば、過去90日まで遡ることができます。 ・A/Bテスト開始前。大きな収益の差が無さそう。 ・A/Bテスト開始後。何度かリフトしている様子が確認できます。 今回のA/Bテストを用いたレポート・評価によって、全ユーザーに開放した場合の収益のインパクトが推測することができ、レコメンドの費用対効果の試算が可能になりました。 おわりに 統計・機械学習を用いたプロダクトはリリースするまで効果が分かりませんし、リリースしたものが事業指標に繋がる保証はありません。そしてリリースしても効果の因果関係は見えづらいです。 そのため、適切な指標を元にリリース後に継続したモデル評価が必要になりますが、そのフェーズで因果推論としてのA/Bテストが有効活用できると実感できました。 また費用対効果の推定だけでなく「投資対効果」という幅を持たせた考え方も大事だと思います。 ひとつの機能レベルでシビアに費用対効果を考えてしまうと、なかなか成果を出すのが難しいと感じています。特にECサイトではトランザクション・売り上げを向上させるのは相当に難しいです。 そのため例えばUXという文脈で[エンゲージメント指標の向上]も判断基準に含めたり、単体のプロダクトではなく[分析・データ活用のプロジェクト]という視点で、広く捉える必要があるとも感じました。 備考 A/Bテストの結果は将来の指標を保証するものではありません。 複数画面で同一パターンを反映させるためにユーザーリストを利用していますが、今回の構成はちょっと冗長でした。各画面にHTML差し込み用の共通のDOMを用意したり、Cookieにテストパターンを保持するなどして、ユーザーリストを使わずとも複数画面でパターンを固定する事は可能です。 Google Analytics360はGoogle Optimize360のみ接続が可能です。つまり有償版Analyticsと無償版Optimizeを接続することはできません。 参考文献 データ分析の力 因果関係に迫る思考法 (光文社新書) セグメントについて - アナリティクス ヘルプ セグメントからユーザーリストを作成する - アナリティクス ヘルプ ユーザーリストを作成、編集する - アナリティクス ヘルプ
アバター
フロントエンド開発部の鳥居です。 サービスのKPIや施策の効果測定などを目的にAnalyticsツールはよく使われますが、Webとモバイルアプリが共存しているサービスでは、WebにGoogle Analytics(GA)、モバイルアプリにFirebase Analytics(FA)と、2つのAnalyticsツールが使われているケースがよくあります。 GAとFAの機能の差異から、分析の際に、GAの機能がFAで使えない、主にフィルタやセグメンテーションの機能が弱く、同じ条件でフィルタができないといった課題がありました。 この課題から、Google Tag Manager(以下GTM)を利用し、FAのデータをGAに転送してGA上でデータを一元的に扱う方法を試してみたので紹介します。 GAとFAの機能の差異から、マーケティング担当の視点では、GAの機能がFAで使えない、主にフィルタやセグメンテーションの機能が弱く、同じ指標で分析が難しいといった課題があるようです。 この課題から、GTMを利用し、FAのデータをGAに転送してGA上でデータを一元的に扱う方法を試してみたので紹介します。 この投稿では、GTM を導入して、アプリからFAのイベントを発行し、GAのコンソールで発行されたイベントを確認するところまでを扱います。 Google Tag Manager FAで発行されたイベントをトリガーしてGAにタグ付けするために利用します。 1.Google Tag Managerのコンテナの作成 はじめにGTMのコンソールからコンテナを作成します。 GTMにはトリガー・変数・タグの要素があり、その他の関連設定とまとめたものをコンテナと呼びます。 トリガー:「Firebaseで発行されたイベントのイベント名が"tapButton"だったら」のように、タグ付けする条件を表します。 変数: イベントに含まれる情報などの値を表します。 タグ: トリガーを条件にGAに変数を含めたイベントを送るといったタグ付けの条件や内容をまとめたものです。 後述しますが、FAからは下記のようなイベントを発行するので、これに合わせてタグを作る手順を記載します。 //イベント名: tapButton //パラメータ: ["button_name": "buttonA"] /* iOS */ Analytics.logEvent("tapButton", parameters: ["button_name": "buttonA"]) /* Android */ Bundle params = new Bundle(); params.putString("button_name", buttonA); mFirebaseAnalytics.logEvent("tapButton", params); 2.トリガーの作成 ワークスペース -> トリガー -> 新規 イベント名が “tapButton"と等しい場合に発火されるトリガーを作成します トリガーの種類: カスタム このトリガーの発生場所: Event Name, 等しい, tapButton 3.変数の作成 同様に変数を2つ新規作成します。 ボタンの名前を扱う変数を作成 ワークスペース -> 変数 -> 新規 イベントのbutton_nameの値(buttonAなど)を扱うための変数を作成します。 変数名: ButtonName 変数の種類: イベントパラメータ EventType: Custom Parameter イベントパラメーターキー: button_name GAのトラッキングIDの変数(UA-xxx-x)を作成 ワークスペース -> 変数 -> 新規 GAのトラッキングIDを扱うための変数を作成します。 変数名: UA-xxx-x 変数の種類: Googleアナリティクス設定 トラッキングID: UA-xxx-x 4.タグの作成 ワークスペース -> タグ -> 新規 先程作成したトリガーと変数を使用してタグを作成します。 タグ名: TapButtonTag タグタイプ: Google アナリティクス - ユニバーサル アナリティクス トラッキングタイプ: イベント カテゴリ: Button アクション: Tap ラベル: {{ButtonName}} 値: 任意 Googleアナリティクス設定 {{UA-xxx-x}} トリガー: TapButtonTrigger トラッキングタイプやトラッキングパラメータはGA側でどのように扱いたいかの設定になるので必要に応じて変更します。 例えば、トラッキングタイプをスクリーンビューとすると、FAのイベントを GAではスクリーンビューとして扱われます。 5.コンテナを公開 コンソールから編集したバージョンのコンテナを公開し、コンテナファイルをダウンロードします。 アプリへの導入 1.Google Tag Manager SDKのインストール アプリにGTM SDKをインストールし、 ダウンロードしたコンテナファイルをXcode上から追加します。 下記の公式のドキュメントに従えば問題なくインストールできます。 iOS https://developers.google.com/tag-manager/ios/v5/ Android https://developers.google.com/tag-manager/android/v5/ 2.Firebase SDKのインストール Firebase SDKをインストールします。 既にFirebaseを導入しているアプリケーションはこの部分は不要です。 iOS https://firebase.google.com/docs/analytics/ios/start?hl=ja Android https://firebase.google.com/docs/analytics/android/start/?hl=ja 3.Firebaseカスタムイベントの作成 カスタムイベントを作成します。 今回は2つボタンを作り、ボタンタップで下記のイベントを発行します。イベント名は tapButton として button_name フィールドの値に buttonA , buttonB としてボタン名に違いをつけています。 /* iOS */ //ボタンA Analytics.logEvent("tapButton", parameters: ["button_name": "buttonA"]) //ボタンB Analytics.logEvent("tapButton", parameters: ["button_name": "buttonB"]) /* Android */ //ボタンA Bundle params = new Bundle(); params.putString("button_name", buttonA); mFirebaseAnalytics.logEvent("tapButton", params); //ボタンB Bundle params = new Bundle(); params.putString("button_name", buttonB); mFirebaseAnalytics.logEvent("tapButton", params); 4.Firebaseデバッグモード FAのイベントが即時に発行されるよう下記の設定でデバッグモードにしておきます。 デバッグモードでない場合、イベントがバッチ処理で発行されるため確認に少し時間がかかります。 https://firebase.google.com/docs/analytics/debugview?hl=ja 発行されたイベントの確認 アプリを起動してFAイベントを発行させます。 それぞれボタンを押した場合のログです。 Firebase Analytics tapbuttonイベントのbutton_nameパラメータがそれぞれbuttonA buttonBとなっていることが確認できます。 Google Analytics FAで発行したイベントがGAで確認できました。 button_name パラメータも正しく表示されています。 備考 導入の工数 GTMを導入をする場合、下記の2つの工数が追加でかかることを考慮する必要があります。 アプリへのSDK導入 既にFAが導入されている場合、GTM SDKのインストールのみです。 変更が少ないので、リスクと工数は低く収まります。 GTMのコンテナの作成とアップデート GTM コンテナを更新するための運用コストが追加で発生します。 例えば、新しいイベントを追加した場合、GTMコンテナにもそのイベントのタグを追加する必要があります。 イベントの種類に比例して工数が増えると考えられます。 Tips コンテナのバージョンを上げた場合、コンテナファイルを差しかえて一度アプリを削除してから実行すると即時に新しいバージョンのGTMコンテナが使用できます。 GAのタグの「値」パラメータは数値のため、数値以外を設定するとイベントに表示されません。 まとめ GTMを導入することで、FAで計測したイベントをGA上で扱えるようになりました。 FAはGAに比べるとまだ機能が少なく、表示したい形式でデータが表示できないことがよくあり、そのような場合に有用な方法と考えています。 今回は、ボタンタップの簡単なイベントで紹介しましたが、 タグの詳細設定のパラメータを変更することで、 FAの自動発行イベントや、イベントに含まれるユーザプロパティを扱うなど、柔軟なデータ連携ができます。
アバター
こんにちは。システム本部の苅部です。 BIツールの評価にあたって、WebpageTestとGoogleAnalyticsのそれぞれのデータを利用してTableauで可視化のイメージを掴んでみましたので、 一連の流れを備忘録として残しておきます。 その1 WebPagetestのデータを可視化する WebPagetestで定点観測しているデータについて、Tableauを使って時系列データおよび散布図として可視化していきます。 WebPagetestとGoogleSpreadSheetの連携については以前の記事をご確認ください。 ・DataStudioとGASでWebPagetestの計測結果をグラフ化する 1. GoogleSpreadSheetを選択 まずデータソースとしてGoogleSpreadSheetを選択します。 Google側の認可画面でGoogleDriveへのアクセスを許可し、任意のシートを選択します。 以下のような形でインポートできました。 日付が文字列として認識されているので、時系列データとして扱えるように日付型に変換しておきます。 2. ざっくり下地を作成 次に新規のシートを作成してグラフを可視化を進めていきます。 SpreadSheetのグラフ機能やDataStudioと比較するとTableauは軽快で使いやすく、直感的に操作をしても見た目が整います。 また、データソースがデータウェアハウスや数百万行のCSVファイルといった巨大な場合でも、予め抽出しておくことで高速なクエリエンジン(Hyper)を利用して非常に早く処理することができます。 3. 微調整して完成 必要に応じてNULLの除去や目盛りの最大値の調整等をすれば完成です。 TimeToFirstByte,DOMInteractive,FirstPaint,onLoad,SpeedIndexの指標を、時系列として綺麗に可視化できました。 TableauOnline/Serverであればデータ主導アラートを使って「特定の閾値を超えたらアラートメールを送信」といった設定もできるはずです。 FirstPaintを説明変数、SpeedIndexを目的変数として散布図も作成してみました。 ツールヒントにはR2乗値とp値が表示されますので、回帰式の当てはまりの良さを把握することができます。 その2 GoogleAnalyticsのデータを地図に重ねる GoogleAnalyticsの速度指標を使って地域ごとのWebパフォーマンスをTableauでマッピングしていきます。 1. GoogleAnalyticsを選択 データソースとしてGoogleAnalyticsを選択します。 Google側の認可画面でGoogleAnalyticsへのアクセスを許可し、任意のシートを選択します。 今回は以下のような形でGoogleAnalyticsのCoreReportingAPIからデータを取得しました。 2. 地域名を正規化して地理データへ GoogleAnalyticsの地域情報には[Kanagawa Prefecture]といった形で[Prefecture]の文字列が入っていることがあるので、Tableauの計算フィールドを使って正規化しておきます。 if CONTAINS([地域],"Prefecture") = FALSE then [地域] ELSE REGEXP_REPLACE([地域],"Prefecture","") END ※地域ディメンションの"Prefecture"の文字列を取り除いています。 そして、このディメンションは都道府県の地理データとして認識させたいので、[地理的役割]から[都道府県/州]を選んでおきます。 3. データを地図と重ねる さきほど計算フィールドで作成したディメンションと任意のメジャーを選択して記号マップを作成します。 今回はディメンションとしてページ読み込み時間(onLoad)を選択しました。 4. 配色を変化させる 全て同じ色で地域ごとの指標の差が分かりづらいので、メジャーの値を利用して色を変化させます。 5. 指標を中央値に変更する 記号の色も大きさも指標が[合計値]となっているため、それぞれ[中央値]に変更します。 ※合計値の場合、単純にレコード数の多い都道府県が大きくなってしまいます。 6. 色を変更する 青のグラデーションでは指標の良し悪しがわからないため、赤から緑の分化に変更しさらに反転をかけておきます。 onLoadの指標が芳しくない沖縄/東北が赤い丸となり、より目立つようになりました。 ということで、まずは1つの目のシートの完成です。 8. ダッシュボードの作成 さらに同じ流れで合計4つのシートを作成し、1つのダッシュボードにまとめて一覧表示させました。 残り3つのシートは、それぞれDOMInteractive/onLoad/DOMContentLoaded/サーバ応答としています。 沖縄県はどの値も悪そうで、北陸や東北の一部の地域も芳しくありません。 直接的な原因は分かりませんが、各Webサーバとの物理的な距離とそれによるレイテンシ影響によって地理的に不利ではありそうです。 GoogleAnalyticsから取得できる指標は、日本中からアクセスしている実際のユーザーの値ですので、地図と指標を重ねることで地域ごとのパフォーマンスを可視化することができました。 備考 レイテンシ以外にも[地域ごとの携帯端末の利用状況の違い]だったり[WiFi普及状況]といった他の要因もあると思います。 モバイルネットワークでのIPアドレス地域判定の精度は高くありません。通信事業者によっては東京にいても大阪判定されるケースがあります。 GoogleAnalyticsのCoreReportingAPIは、セグメントの掛け合わせ方によっては精度が落ちることがあります。 おまけ TableauはMapboxとの連携も可能です。 MapboxのAPIキーをTableau側で入力することで、任意のマップを使うこともできます。 おわりに WebPagetestやGoogleAnalyticsのデータを使って、Tableauの基本的な操作感を掴むことができました。 可視化という意味では、他BIツールであったり、GoogleAnalytics、GoogleSpreadSheet、Excel、DataStudio、Re:Dash、Grafana、R、Pythonなども選択肢としてありますが、[個人個人が探索的に分析してデータを理解していく]という目的においては、機能面と操作性でTableauがバランスが取れていると思いました。 TableauはUIが直感的でデータの抽出速度も速いため可視化のコストが低い印象です。 今回TableauDesktopで完結させていますが、TableauOnline/Serverでブラウザ上で分析できる環境を整えると、データが民主化され一歩進んだ分析が可能になると思います。 参考URL Tableau テクノロジー | Tableau Software Hyper | Tableau Software Mapbox マップの使用 Tableau Online または Tableau Server からのデータ主導アラートの送信 TableauでGoogle アナリティクスをデータソースとするときのコツ Googleアナリティクス x Tableau(タブロー)ことはじめ  心得7か条
アバター
お久しぶりです、インフラストラクチャー部の沼沢です。 久しぶりのくせに今回は小ネタです。 OpsWorks で管理している EC2 インスタンスのログを、CloudWatch Logs に転送する場合、以前は自前で Chef Recipe を用意して Execute Recipe をする必要がありました。 ただ、(いつからかは把握できてませんが) 現在は、OpsWorks コンソールや CLI だけで簡単に設定できるようになっています。 今回はこれを試してみた結果の共有と、注意点を書き記しておきます。 前提条件 今回試した環境は以下の通りです。 記載のないものは適当に設定していただければ問題無いと思います。 Stack Chef version: 11.10 OpsWorks Agent version: 3449 (Jun 5th 2018) Default IAM instance profile: AWSOpsWorksCloudWatchLogs が付与されているプロファイルを指定 Layer PHP App Server Network Public IP Addresses: Yes Security(以下のセキュリティグループを指定) Inbound: ルールなし(全拒否) Outbound: 制限無し(全許可) Instance Amazon Linux 2017.09 執筆時点で、OpsWorks で指定できる Amazon Linux の最新バージョン 今回は2台用意し、起動済みとする Hostname は numatest01 , numatest02 とした 試してみる AWS OpsWorks スタックでの Amazon CloudWatch Logs の使用 上記の公式ドキュメントに書いてある通りではありますが、これを試してみたいと思います。 たったこれだけです。 この設定をしたあと、数分待つと CloudWatch Logs にログが転送され始めます。 ちゃんと出ていました!ログの中身も見てみましょう。 注意点 これ、とても楽チンでとても便利なんですが、見ての通り、画面からは CloudWatch Logs を On にするか Stream command Logs を Yes (出力)にするか 出力したいログファイルのパスを定義 この3つの設定しかできないため、画面からでは本来 CloudWatch Logs でできるはずの細かい設定はできないようです。 CloudWatch Logs エージェントのリファレンス 画面から設定を行っただけの状態の /var/awslogs/etc/awslogs.conf の中身はこんな感じでした。 [general] state_file = /var/awslogs/state/agent-state [numa_cwlogs_test/php-app/opsworks-command-log /var/lib/aws/opsworks/chef/*.log] log_stream_name = numatest01 file = /var/lib/aws/opsworks/chef/*.log log_group_name = numa_cwlogs_test/php-app/opsworks-command-log [numa_cwlogs_test/php-app/var/log/messages /var/log/messages] log_stream_name = numatest01 file = /var/log/messages log_group_name = numa_cwlogs_test/php-app/var/log/messages [numa_cwlogs_test/php-app/var/log/cron /var/log/cron] log_stream_name = numatest01 file = /var/log/cron log_group_name = numa_cwlogs_test/php-app/var/log/cron [numa_cwlogs_test/php-app/var/log/secure /var/log/secure] log_stream_name = numatest01 file = /var/log/secure log_group_name = numa_cwlogs_test/php-app/var/log/secure これを細かく設定したい場合は、CLI で設定するか、従来通り自前で Chef Recipe を用意して Execute Recipe をする必要があります。 CLI は、 aws opsworks update-layer のドキュメントを確認したところ、 --cloud-watch-logs-configuration というオプションで細かく指定できるようです。 あとがき 個人的には画面から行える設定だけで十分感があります。 いずれ、細かな設定も画面から行えるようになるとさらに楽で良いですね!
アバター
こんにちは、デザイナーの渡邉です。 早いもので新卒入社3年目となり、新卒1年目の学びをここで記事にしてから1年が経ってしまいました。 (前回は「新卒1年目が終わりました!」なんて新米感たっぷりなのに、たった1年で「入社3年目になりました!」だなんて、急に上級生みたいで不思議ですね) 前回の記事はこちら → 「新人Webデザイナーが1年の学びを振り返る」 さてこの記事では、ついに後輩を持つことになった私が今年どんな人材であるべきか、自身の新卒時代の経験を元に考えてみようと思います。 自分の後を歩いている学生さんや、3年目の悩みを抱えている私と同じような経験の浅いデザイナーさんの参考になれば嬉しいです。(自分への言い聞かせと備忘録も兼ねてます。) では早速 ♪ 目次 良い先輩ってなんだろう ダメな先輩ってなんだろう これに注意しながら3年目に挑戦すること 最後に 良い先輩ってなんだろう 話しかけやすい雰囲気を持とう きっと後輩やチームを持つと忙しくなりますが、集中したくてもフリースペースで仕事をしていたりいつもイヤホンをつけて仕事をしていたら、新人のホットな質問も冷えてしまいますよね。 “わからないコトをわからないママにさせない” 環境作りを大事にしたいです。 (あ、でも一部のエンジニアさんは、ヘッドフォンで音楽を聴いている最中は話しかけない方がいいらしいです。) ちゃんと時間を割いてヒントを与えてあげよう 片手間の対応では、どうしても目先の課題解決が優先されて根本的な技術力向上にならないことがあります。 それに、技術を学べたとしても事業に込めた想いまでは学べないですよね。教育の分の余剰を確保している余裕が大切だと思ってます。 でも切羽詰まってる時には答えまで導いてあげよう とはいえ、ひとつの課題に時間をかけ過ぎないのも大事です。デザイナーにとっては社外のインプットの時間も大切ですし、遅くなる前に終わらせて、翌日新しい課題でそれが活かせればOKだと私は思います。 気づきや学びをまとめさせよう 私は1年半毎日日報を書かされて、今日やる予定だったのにできなかったこと、その理由、気づき、新しく知った言葉などをまとめていました。当時は面倒になったこともありましたが、今となってはスキルレベルを測る貴重な資料です。経験から得た気づきはまとめさせるべきだと思います。 小さなことにも努力を惜しまない姿勢を見せるよう 弊社のCREDO(企業理念に代わるもの)にあった言葉ですが “ここまでやる"は伝わり"これくらいでいいや"も伝わる から 想像を超えて感動を与えよう! ということです。 ちなみに今年度よりCREDOが刷新されております。→ 新しくなったmedibaのCREDO ⇒つまり育てる意識がある先輩ってカッコいい!って思ってます。 やらせるだけでなく、模範を見せよう デザインをするときに過去作品やサンプルがあった方が、トーンのイメージが湧きますし、事務作業においても、早く事故なく覚えられると思っています。 挨拶は自分から、説明は例を添えて、指摘するときは理由付きで! 任せる姿勢を持とう 早い段階から、自分で考えて1つのタスクを完遂させる機会を与えたいです。ついつい小さな仕事ばかり振ってしまいそうですが、すでに自分の成果物の対価としてお給料を戴いている「プロ」なので、その自覚を持って欲しいと思います。 もちろん、まずは自分に責任感があることですが、次に相手にもそれを持たせる心構えが早い段階から必要かと! 結果だけじゃなくプロセスも見よう 新人がすぐに結果を出すのは簡単ではありません。なので達成までのプロセスも把握して、そこに対しても評価したり褒めたりできると、成長を後押しできると感じています。 それに、中期的なプロジェクトの途中段階でも成果に向けた学習や、縁の下の力持ちになった行動に対して、公正な評価ができます。 ミスはしっかり謝罪しよう 前と違うことを言ってしまった。教えた内容に誤りがあった。指示にミスがあった…。ちゃんと謝りましょう。それと、新人がうっかりミスやってしまったときは一緒に上に謝りに行きましょうね。 目指したい方向性を理解しよう 本人のキャリアプランを汲み取った上で成長につながるタスクを振ることができると、やりたい事とやらせてもらえる事が合致するのでモチベーションが上がりますね!合わせて、得意領域を深掘りさせるのも有用です。 ⇒つまりモチベーション管理ができる先輩ってカッコいい!って思ってます。 ダメな先輩ってなんだろう 後輩に自身への評価を求める 一番近くで見ているはずなのに、後輩は先輩の大変さに気づいていなかったりするものです。(きっと私もそうでした。)ですがその頑張りはさらに自分の先輩に見てもらうとして、後輩には自身の成長に集中してもらいましょう。 後輩やチームメンバーに嫉妬する 仕事のゴールは良いサービスがお客さんの手に届くことです。 相談しに来た人と一緒になって悩んでしまう なぜ自分の元に相談に来たのか、役割を考えましょう。もし自分の方が上の立ち位置だったらビシッと決めてしまって良いと思います。決められない場合は一緒に上長へ! 簡単な仕事しかしてないから暇だろうと思い込む 新人って、いろんな部の人と急速に知り合いますし、いろんな雑務をこなしながら仕事の勉強もするので結構忙しいのです…。 アンテナの感度が低い みんなが知ってる情報に気づけないのはちょっとカッコ悪い。 仲介なのに上流の意見を汲み取れていない 下流からの指摘で確認漏れに気づくのは時間の無駄も大きくスマートじゃないですね。 それぞれの目的、意図、優先度などは必ず確認しようと思います。 会社の愚痴を言う フレッシュな新人のモチベを下げてしまいますね。長く働けば不満のひとつもあるかもしれませんが、自分の工夫で改善できないかを探したり、相談はまず先輩や上司に言うほうがスマートかと! 自分のやり方に固執する 学びを下の世代に受け継ぐのは大切です。でもマイルールこだわって、本来のゴールを見失わないようにしましょう! これに注意しながら3年目に挑戦すること 後輩に対して ・基礎的なツールとサービス内容を覚えてもらう ・勉強会を開く ・タスクの進捗や仕事への関心、心持ちなどをヒアリングをする ・やりがいのある仕事を与える 自分に対して ・積極的にユーザーになりUIやUXを学ぶ ・ユーザーの声を聞く ・チームメンバーからFBをもらう 最後に とはいえまだ3年目!! →ようやく人並みの仕事をさせてもらえるようになったところで、まだ先輩や上司の手を借りることも少なく無いです。 後輩社員が入社してくると、不思議と「自分は先輩だ」「仕事はできる」という気になってきますが、素直に謙虚に、自分もこれまで同様に成長に注力したいです。(不要な仕事論とか説かないようにしたいですねw)
アバター
久々の投稿になります。 medibaの松本です。 弊社では、外部技術顧問として3名の方にご協力いただいております。 主に、採用や技術戦略の相談といったものや、最新の業界動向などを相談させてもらっています。 今回は、その中で主に組織作りを中心に相談させてもらっている井原さん(ビットジャーニーCEO)に、以下のテーマで弊社のエンジニア向けにお話をしてもらいましたので、紹介しようと思います。 弊社は若手から中堅のエンジニアが多いので、井原さんの経験から少しでも得るものがあればいいなと思いました。 井原さんについて 株式会社ビットジャーニー を起業、CEOとして活躍するかたわら、様々な企業の技術顧問を務める。 現在は、「ひとりのアイデアをみんなのチカラに」をうたう情報共有ツール「Kibela」を開発、リリース。 お話いただいたテーマ 「井原さんが、それぞれの人生のターニングポイントにおいて何を考えてどう行動したのか」 井原さんが、これまでどういうことを考えて何をやってきたのかを、自身の経験を交えながらお話いただきました。 エンジニアにとっては、転職や起業は他人ごとではなく、かつインパクトの大きいイベントなので、始める前から期待感MAXでした。 中身の抜粋 都合により、お昼時の開催となったにもかかわらず、弊社のエンジニアが20名程度参加しての開催となりました。 当初、井原さん主導でお話いただく形を想定していましたが、進行の関係で、急遽私がインタビュアー役として対談形式で進めることになりました。 抜粋になりますが、以下のような感じで進みました。 クックパッドへ転職するに当たって考えたこと 何がやりたくて転職したのか クックパッドでは開発部長として何をしたのか どんな組織を作ろうと思ったのか エンジニアを採用する基準、どんなエンジニアを欲しいと思ったのか 死ぬほど辛かったこととか なぜ辞めて、起業しようと思ったのか ビットジャーニーを起業するに当たって どんなことがやりたくて会社を起こそうと思ったのか 家族の反対はなかったか 採用担当として どんな人を取ろうとしていたか どんなやり方でやったのか 「世界で一番エンジニアが幸せな組織を作る」 目標のもと工夫された話や、強い組織を作るための秘訣などが非常に印象的でした。 また他にも、非常に考えさせられるお話がたくさんありました。 何がエンジニアの士気を奪うのか 何をすると成功するかはわからなかったが、何をすると失敗するのかはわかるようになった そのやり方は、自分は幸せでも、他人(チームメンバーとか)にとっては幸せなのか? 意思決定を自分の方に持って来るために、 選択肢を増やすための信頼 を作ること アウトプットは、必ず求められているものの+1でもいいから付加価値をつけて出す またマラソン好きな井原さんらしく、目の前の壁を越えるためのお話も、マラソンになぞらえて話をしてくれました。 ぶっちゃけ、折れない心と壊れない身体さえあれば、なんとでもなります。 (By井原さん) 最後に というわけで、いわゆる勉強会とは毛色を変えた、社内の風景をBlogにしてみました。 社内のエンジニア陣にも好評で、また機会があればこのような催しを実施してみたいと思います。 何よりも、お忙しい中快く承諾してくださった井原さん、本当にありがとうございました。 最後に宣伝。 medibaでは、広くエンジニアを募集しています。 ご興味を持っていただけましたら、ぜひ 募集要項 など確認いただき、ご連絡ください!
アバター
メディアシステム開発部の野崎です。 メディアシステム開発部では、「 auWebポータル 」や「 auスマートパス 」といった、サービスを担当しています。 弊社では一部のサービスでアクセスログなどをTreasure Dataに貯めています。 今後はこのデータを分析活用し、より良いサービスを提供していきたいと考えています。 その一歩として、今回はTreasure Data内で使える機械学習ライブラリhivemallを利用してユーザレコメンドを試してみました。 はじめに 今回は、DACさんのブログを参考にさせていただきました。 「HivemallでMinhash!〜似てる記事を探し出そう。〜」 ※ Treasure Dataのブログにも英訳されている、非常にわかりやすい記事です! この参考記事をなぞる形ですが、サンプルデータではなく、実サービスのデータを入力とします。 あるサービスのユーザごとのジャンル別の閲覧数を入力とし、 あるユーザに閲覧傾向が似ているユーザをレコメンドしてみます。 Minhashとは minhashは、乱択アルゴリズムと呼ばれます。 大量のユーザデータや文書データを比較したい場合に利用され、 類似しているユーザや文書を推定することができます。 ある2つの集合(ユーザや文章)の類似度は、 Jaccard係数で表すことができます。 Jaccard係数は、積集合を和集合で割った値で表されます。 参考記事にあるように、 2つの集合の要素にあるhash関数を適用しその結果のうち最小値が一致する確率と Jaccard係数は等しくなる性質があります。 この際に使うhash関数をk個用意すれば、 最小値が一致した個数をkで割ればJaccard係数を推定できる。 つまり、2つの集合の類似度を推定できるということになります。 利用する環境 今回利用した環境は以下の通りです。 hive : 0.13.0 参考 hivemall : 0.4.2-rc.4 利用する関数の確認 minhashを使います。 関数の定義は、 hivemallのユーザガイド を参考にしながら hiveの DESCRIBE FUNCTION を使って確認するのがわかりやすいです。 (minhash) 第一引数に、 item 、第二引数に、 features を取ります。 また、オプションとしてnを取ります。 今回は、itemをuser_idとします。 返り値として、一つのuser_idに対して、n個のhash値が出力されます。 次に、featuresを用意します。 入力データの用意 ユーザガイドのinput ページを参考にします。 featuresは、特徴量ベクトルで、indexのみ、またはindexとweightをセットで表します。 hivemallでは、これらをダブルクオーテーションで括った形式で表します。 上記のfeature関数を使います。 とあるサービスの、 user_idに対し、ジャンルごとの閲覧数を合計した テーブル userid_by_genre があるので、それを元にします。 user_id genre_id_1 genre_id_2 genre_id_3 1 0 2 10 2 1 4 12 3 0 8 1 4 10 4 0 5 9 7 12 ※記載してあるデータはサンプルです。 このテーブルに対して、 以下のようなクエリでfeature関数を適応します。 (type:hive) SELECT user_id AS user_id, ARRAY(FEATURE(1, genre_id_1), FEATURE(2, genre_id_2), FEATURE(3, genre_id_3)) AS feature FROM userid_by_genre 以下のような、 user_idとfeatureのデータができます。 これを、別テーブル user_feature に書き出しておきます。 hashの作成 このテーブルに対して、minhash関数を適応していきます。 n=10とし、一つのuser_idに対して、10個のhash値を生成します。 (type:hive) SELECT minhash( user_id, feature, "-n 10" ) AS( clusterId, rowid ) FROM user_feature 以下のような、hash値が生成されます。 この結果を、別テーブル user_hash に書き出しておきます。 類似ユーザの集計 テーブル user_hash の 異なるuser_idからhash値がある個数一致するuser_idを集計します。 今回は、9個以上のものを対象とします。hash関数は10種類あるのでJaccard係数が0.9以上のものとなります。 SELECT J.Rowid, Collect_set(rid) AS Related_userid FROM ( SELECT L.Rowid, R.Rowid AS rid, COUNT(*) AS cnt -- minhashが一致する数 FROM user_hash l LEFT OUTER JOIN user_hash r ON ( L.Clusterid = R.Clusterid ) -- minhashの値が一致するレコードのみをjoin WHERE L.Rowid != R.Rowid --同じuser_idは除外 GROUP BY l.rowid, r.rowid HAVING cnt > 8 -- Jaccard係数が0.8以下のものは除外 ) J GROUP BY J.Rowid ORDER BY J.Rowid ASC ここでは、実データはお見せできませんが、 以下のような形式の結果が得られます。 user_id:1に対して、user_id:2,4,5のユーザの閲覧が似ていることになります。 user_id Related_userid 1 [2,4,5] 3 [7,8] 4 [10,11] この評価に関しても、本記事では詳細を扱いませんが ほぼ同様の閲覧傾向にあるユーザをレコメンドすることができました。 まとめ 参考記事をなぞる形になりましたが、実際のサービスのデータを用いて Treasure Dataのhivamallを用いて、ユーザのレコメンドを試してみました。 どのようなデータを特徴量とするかどうやって特徴量を作るかあたりが戸惑いましたが、 Treasure Data上に生データやhivemallの実行環境が用意されていることで、手軽に試すことができました。 メディアシステム開発部では、Treasure Dataなどを利用してサービスに貢献できるエンジニアを募集しています。 ご興味ある方は、 募集職種一覧 などをご確認ください。
アバター
こんにちは。デザイナーの大村です。 Adobe XD CC や InVision Studio が気になりつつも Sketch をバージョン管理できる、設計チームで一緒に作業するためのプラットフォーム Abstract (0.63.5) を使ってみたところとても便利だなと思ったので紹介します。 Abstract ってどんなもの? デザインデータのバージョン管理ができる 最新データがどれなのかバージョン管理で透明化できる 複数人での並行作業を簡単に一本化できる ファイルを開くことなくプロジェクトの変更点を確認できる https://www.goabstract.com/ 月額費用:Business $16.67 Starter $10 メリット バージョン管理された過去データでもローカルにも落とせる 過去に遡れるのでパーツがなぜ生まれたかコメントを流れで追うことができ、理解しやすい Slack 連携で誰が現在何やってるか〝見える化〟できる Abstract で管理している Sketch ファイルは Contributor(課金利用している人) なら誰でも編集して Commit することもできる コメントのやりとりでデザインレビューできる(Viewer で招待すれば誰でも無料で参加可能) Business であれば 無制限 にバージョン管理ができる(Starter は250GBまで) デメリット Sketch 以外のファイルは管理できない コミニュケーションロスが減ってストレスも軽減 「作って」→「どっかあげて」→「見てねって何かでお知らせして」→「お返事もらって」→「読んで理解して」→「修正して」→「修正したよってお知らせして」→「見てもらう」 みたいなフローが 「Branch 切って」→「作って」→「見てねってお知らせして」→「お返事もらって」→「お返事見ながら修正して」→「Commit =お知らせして」→「見てもらう」 となります。 この「どっかあげて」と言うのが重要で、弊社では Share Point (チームによっては社内の共有サーバや InVision や Prott ) に格納してその場所をお知らせしてレビューのやりとりを行うのですが、人為的な作業なのであげ忘れや上げ漏れがあって「上がってないよ!」「反映されてないよ!」というタイムロスが無くなるのは大きく違うと思います。 またバージョン管理しているので「このデザインってどこの関連だっけ~?」とデザインデータのファイル管理も減り、少し楽になりました。(配置する画像は Photoshop と Illustrator なのでそのデータは別で管理します。) 一番嬉しいことはひとりで悩む時間が減ったこと。 「こんなデザイン思いついたけど、解決方法にマッチしてないかも。でもこう考えるとマッチしてるのかも・・・(悩)とっておこうか捨てようか。」と悩んだ時に、考えた事をそのまま Commit して Comment でメンバーに聞けば、「解決にならないならいらないよ」とか「こういう考え方するとそのデザインで合ってると思う」とか、客観的な意見がその時点で聞けるのでデザインを作りながらひとりで悩む時間が減りました。 コメントはこんな感じで書けます。 @でメンションする(される)と右上のベルとアプリアイコンにバッジがついてお知らせされます。 Slack 連携して「見える化」 Slack をプロジェクトチームで使ってる場合、連携して Commit したことが「見える化」できるので流れが見えて安心です。 Commit の粒度を一定にして自分以外の人も分かりやすくできるとレビューが捗りそうです。 Slack への連携方法 ホームの Organization Setting の Integrations から可能です。 ※Organization Setting は Web からの設定になります。 お知らせしたい Active Project を選択 Organization Setting の Integrations から通知したい Channel を選択 デザイン作業とバージョン管理の親和性 デザインを進める工程で、選択が間違っていたことに気がついても、保管されている Sketch データを開いて過去のデザインデータを拾うことができるので、「後で使うかもしれないから・・」といらないデータを取っておく必要もなく、取捨選択しながらとにかく進めることができます。 別ページや別ファイルでゴミ(いつか使うかもしれない)データを残しておかなくても良いので、いつでも整理されていて見やすく、第三者からレビューもしやすいです。 また、確定しているデザインは常に Master にあるので改修中のデザインと分けて見ることができ安心して他者に展開できます。 Master にあるデータは一覧で確認できる Sketch ファイルを開くことなくプレビューで確認できるので、パソコンの負荷も少なく時短が期待できます。フットワークが軽くなるのも一つの魅力です。 Commit 単位で変更したもの変更してないものが一目でわかる こんな風に見れるので、 Commit の単位は「なぜその変更をしたのか」変更の目的単位にする必要があります。 複数人での並行作業を簡単に一本化できる 自分以外の人が編集した差分データを自分の Branch に取り込んでマスターに Merge することができます。複数人で案を出して部分でいいとこ取りも可能です。また、パーツの過不足や誤字脱字のようなちょとした修正ならフォローし合ってデザインデータの間違えを正していけそうです。 さいごに INVITE で招待メールを送れば、誰でもビューアーになってコメントが書けるので、Design レビューのツールとしても便利です。デザインはユーザーとの接点なので企画側、開発側、ビジネス側全ての観点からレビューを頂いて、届けたい人に届くデザインを作りたいと思っています。
アバター
初めまして。フロントエンドエンジニアの謝花(ジャハナ)です。 入社して8ヶ月ほど経ちましてこうして初めてブログを書きます。 沖縄を離れて長い年月が経ちますが、東京では「ジャハナ」が人名だと認識されず未だに苦しんでおります。(電話ごしだと100%聞き取ってもらえません) そんなこんなで楽しくお仕事させていただいているのですが、最近とあるプロジェクトでHTTP/2を取り入れ、モバイルサイトのページ表示速度の高速化に取り組みましたので、そのお話をフロントエンドの視点からお話します。 表示速度が遅い! 改善したい! と思っていても、実際どこから手をつけていいかいまいち分からないと言う方もいらっしゃるかと思います。 少しでもそんな方の参考になればと思っております。 Webページの表示速度の改善はなぜ必要? もちろん表示速度は速いに越したことはないと誰しもお思いでしょうが、 具体的に表示が遅いとどういったことが起こるのでしょうか? ユーザの約50%が2秒以内のページ表示を期待し、読み込み速度が3秒以上かかると40%のユーザが離脱する 引用元 : https://blog.kissmetrics.com/loading-time/?wide=1 操作開始時間が3秒のサイトは1秒のサイトに比べ、CVRは38%低下、直帰率は50%上昇する 引用元 : http://web-tan.forum.impressrd.jp/e/2014/07/08/17757 んー、どんなにいいサービスやコンテンツが揃っていても表示が遅いとユーザーは離れていってしまうということですね。 ページの表示が遅いというのはまさに百害あって一利なしということが分かります。 表示速度が遅くていい人なんていないですよね。イライラしますもんね。 HTTP1.1の問題点 HTTP1.1では、1つのリクエストが完了するまで、次のリクエストを送ることができなくなっています。 リソースが2つあった場合は、1つ目の読み込みが完了してから2つ目のリソースのリクエストを開始いたします。 引越しで例えるならば、引越し先まで荷物を1つずつしか運べないのと同じ状況になります。 100個の荷物があれば100往復しなければなりません。相当時間がかかりそうですね。 このようにHTTP1.1では、1ホストごとに1つのリクエストしかできないので、リソースが多ければ多いほど往復する数が増え、ページの表示に時間がかかってしまいます。この方法は明らかに非効率です。 これを回避するために、ほとんどのモダンブラウザは1ドメインに対し複数同時接続を行うことで、ある程度通信の多重化を図っています。 次の図は、弊社のWebサイト(HTTP1.1)をChromeブラウザのデバッグツールで確認したものになります。 Chromeが同時に送信するリクエストは最大6つまでである(7つ目以降はブロックする)ことが分かります。 HTTP/2の多重化処理とは HTTP1.1の1つのリクエストが完了するまで、次のリクエストを送ることができないという問題点をHTTP/2ではリクエストを並列に送信することでクリアにしています。 上の図のように、HTTP/2では1つのコネクション上で複数並列に扱うことが可能になりました。 この問題が解決されると次の図のように、ページが読み込み完了(onLoad)されるまでの時間が短縮されます。 さらに、次の図は、弊社が運営している、あるWebサイトのHTTP/2でのリソースのリクエストの様子です。 HTTP1.1と比較していただくとより効率よく、1度にいくつものリソースがリクエストされてるかがお分かりいただけるかと思います。 レンダリングパス最適化について HTTP/2の特徴がなんとなく分かったかと思いますので、ここからは表示速度の向上のため、より効率的なHTML + CSSの設計について考えていきます。 HTTP1.1時代のCSS設計 HTTP/1.1の場合は一度に通信できる量が限られているので、以下のサンプルのようにリクエスト数を減らすためなるべくまとめてリクエストをするのが主流でした。 <html> <head> ... <!-- リクエスト数削減のため一つにまとめたcssファイル --> <link href=“all.css”> </head> <body> ... </body> </html> 他にもCSSスプライトなどリクエスト数を極力減らすような設計をしたことあるのではないでしょうか? しかし、HTTP/2の場合だと先述したように並列でのリクエストが可能になったため、このような設計は効果的ではありません。 リクエストが並列で行われるということは 無理にリソースを1つにまとめる必要がなくなりました。 むしろ無理に1つにまとめ、ファイルサイズを大きくしてしまうとレスポンスが返ってくる時間が長くなり逆に表示速度が遅くなってしまいます。 HTTP/2時代のCSS設計 以上を踏まえて、実際にHTTP/2を使用したページのCSSについてです。 <html> <head> ... <!-- ヘッダのcssファイル --> <link href=“header.css”> <!-- メインコンテンツのcssファイル --> <link href=“main.css”> <!-- フッタのcssファイル --> <link href=“footer.css”> </head> <body> ... </body> </html> 上記のように1つにまとめるのではなく、なるべく細かく分けることがポイントです。 先述してるように、リソースを無理にまとめなくてよくなったというのももちろんですが、 コンポーネント指向という観点や、キャッシュを持たせるという意味でもCSSの細分化は必要と言えます。 結果 上記を踏まえて実際にページの表示速度はどう変わるのかChromeのデバッグツールで検証してみました。 ①HTTP1.1 ②HTTP/2 着目すべきは描画が開始されるまでの時間です。 ①の 1.89s に対し、②は 901ms となっております。 時間が短くなるにつれ、ユーザの待機時間が減りますので表示の体感速度は上昇してると言えるでしょう。 今回はそこまでリソースが多くないページでの検証になりましたが、リソースが比較的多い大規模なページであれば、より差がつくはずです。 ※この検証は差を出すため、意図的に遅めのネットワーク環境で試しております。 しかし、なんでもかんでもHTTP/2にすれば表示速度が速くなるかと言われるとそうではありません。 HTTP/2はリクエスト数が多ければ多いほど効果を発揮しますので、 そもそもリクエスト数やリソースが少ないサイトではそれほど効果は見られない でしょう。 まとめ HTTP1.1は1ホストごとに1つのリクエストしかできない Chromeなどのモダンブラウザでは同時に送信するリクエストは6つまで HTTP/2は複数並列にリクエストすることが可能 HTTP/2ではリクエストが並列でなされるため、なるべくリソースを細分化してファイルサイズを小さくすると効果的 リクエスト数やリソースの少ないサイトでは効果は薄い また、本日は触れておりませんが、HTTP/2ではリクエストのプライオリティも指定できます。 ですので、優先度の高いリソースから先にリクエストすることが可能になりました。 HTTP/2のプライオリティ制御について 加えて、ページの表示速度を向上させるにはCSSをいかに早く返してもらえるかが鍵となります。 ですので外部ファイルを読み込むのではなく、HTML内に直接CSSをインライン化し、レンダリングまでの時間をなくしてしまうのも効果的です。 インライン化する際は、しっかり設計をしていないとメンテナンスが大変になりそうですが、そういうWebサイトも今後増えてくるのではないでしょうか。 おまけ Chrome Canaryの chrome://flags のランタイムフラグの中に Experimental Web Platform features というのがあり、 そのフラグをONにすればCSS in body を試すことができます。 CSS in bodyにした時のメリットは以下。 無駄なCSSの読み込みを待たずともレンダリングが可能になるのでとても効率的 全てのCSSを読み込んでから一気にレンダリングということがなくなり、準備ができたものから少しずつ表示されるので表示の体感速度は向上する <html> <head> ... </head> <body> <!-- ヘッダのcssファイル --> <link href=“header.css”> <header> ... </header> <!-- メインコンテンツのcssファイル --> <link href=“main.css”> <main> ... </main> <!-- フッタのcssファイル --> <link href=“footer.css”> <footer> ... </footer> </body> </html> 上記の設計であれば、それぞれ必要なCSSが読み込まれた直後にレンダリングが開始されます。 不要なCSSの読み込みを無駄に待ったりしませんので、表示速度が飛躍的に向上します。 そのCSS in Bodyを以下のHTMLで実際に検証してみました。 ・HTML <html> <head> ... </head> <body> <link href=“reset.min.css”> <link href=“definition.min.css”> <!-- ヘッダのcssファイル --> <link href=“header.min.css”> <header> ... </header> <!-- メインコンテンツのcssファイル --> <link href=“top_main.min.css”> <main> <img src=“101.png”> <img src=“211.png”> <img src=“600.png”> <img src=“100.png”> <img src=“500.png”> <img src=“200.png"> </main> <!-- weeklyのcssファイル --> <link href=“weekly.min.css”> <div> <img src=“202.png”> <img src=“101.png”> <img src=“201.png”> </div> </body> </html> ・リクエストの様子 top_main.min.css を読み込んだ後に、 <main> の中の画像を読み込んでいますね。 さらに、 <main> のレンダリングが終われば次にある weekly.min.css を読み込んでいますので、必要なCSSを読み込んだ後にレンダリングがされているのが分かります。 ・描画の様子 まとめてレンダリングされることはなく、 <header> から優先してレンダリングされているのが確認できますので、レンダリングパスの最適化がされているといえるでしょう。 本日は以上となります。ありがとうございました。 参考 : https://jakearchibald.com/2016/link-in-body/
アバター
こんにちは。制作部の苅部です。 今回は、サービス横断でのWebパフォーマンス改善を1年間続けた中で指標としてSpeed Indexを採用した振り返りを書き残しておこうと思います。 Speed Indexとは 時間ごとの描画面積で算出される値で、体感速度の指標として参考にすることができます。 UX向上としてのWebパフォーマンス改善を考える時に、他の指標よりも役に立ちます。 DOMContentLoadedやwindow.onload、First Paintといったいくつもの指標はあくまで説明変数で、Speed Indexが目的変数になると考えています。 体感速度における目的変数が明確になることで、実施すべき施策(クリティカルレンダリングパスなど)にフォーカスすることができるようになります。 ※Speed Indexの詳しい算出方法については以下ページが参考になります。 Speed Index - WebPagetest Documentation Speed Index – how it works and what it means - NCC Group 改善の進め方 1. 数値を定量化する Speed Indexを統計的な定量データにした上で、時系列の軸で他のデータと比較できるようにします。 これによって「数値」を「ファクト」として扱えるようになります。 数値の妥当性を計るためにもヒストグラムも合わせて確認できるようにすると良いと思います。 2. 比較のためのベンチマークをとる 速度とは相対的な値ですので、比較のために同じ条件でベンチマークを複数取り相対評価をできるようにします。 これで速い・遅いの判断ができるようになります。 3. 改善して効果を測る ベンチマークと比較した結果遅ければ、ファーストビューにフォーカスして描画をブロックする要因がないかを調べ、そのボトルネックに対して改善を行います。 その後、定量データ(統計量)を前後比較して施策の評価を行います。 この繰り返しでPDCAサイクルを回していきます。 Speed Indexの理想値 計測条件や計測対象のUIあるいは計測時のネットワーク影響によって差が出てしまうため厳密な絶対値は出せませんが、いくつか参考になる値はあります。 ・HTTP Archive Big QueryのPublicなデータセットにHTTP Archiveのデータが保存されているため、数十万件の計測データから任意の統計量を取得することができます。 例えば以下のようなクエリを叩くことで、Speed Indexの中央値が取得できます。 クエリ SELECT NTH(501, quantiles(SpeedIndex,1001)) pages_mobile_speedindex_median, FROM [httparchive:runs.2017_09_01_pages_mobile] SELECT NTH(501, quantiles(SpeedIndex,1001)) pages_speed_index_median, FROM [httparchive:runs.2017_09_01_pages] 実行結果 2017年9月1日の計測(46万件)におけるSpeed Indexの中央値はモバイルサイトで8,025、デスクトップサイトで3,800となりました。 モバイルサイトの方がSpeed Indexが高くなる傾向にあるので、目標とする値はViewportで分けて考える必要がありそうです。 ※ HTTP Archiveは世界中のサイトを計測しているため、ネットワーク(RTT)の影響を受けて数値が高い状態になっていると思います。 参考URL: HTTP Archive + BigQuery = Web Performance Answers - igvita.com ・Google Adsense, Double Click by Google AdsenseのブログやDouble Clickの資料ではSpeed Indexへの言及があり、目指すべき数値として3,000以下と記載されています。 Webpagetest provides a Speed Index that indicates the average time at which visible parts of the page are displayed. Aim for a Speed Index of 3,000 or less and load time of 3 seconds or less — ideally 1-3 seconds.2 5 steps to improve Page Speed and boost page performance ・モバイルサイト認定資格 Google Partnersのモバイルサイト認定資格には Speed Indexのスコアの目標値に関する問題が出てきます。 試験対策ガイドには 私たちの目標は、スコアが 3,000を下回ることです。 2.1.3 目標値の設定 - Google Partners ヘルプ と記載されていて解答欄にも 5,000未満という選択項目が用意されていたので、最低でも5,000未満、理想は3,000未満という解釈ができそうです。 ・書籍:パフォーマンス向上のためのデザイン設計 オライリー・ジャパンから出版されている" パフォーマンス向上のためのデザイン設計 (Designing for Performance)“では、Speed Indexの数値について言及があります。 Table 5-1. Example responsive web design budget Measure Goal Speed Index 1,000 Designing for Performance "Example"となっているので、数値に根拠はないと思いたいです・・。 ・NCC Groupのベンチマーク セキュリティ企業のNCC Groupの記事によれば、英国の小売店(上位50位)のベンチマークは中央値が3,106(帯域:8Mbps)だった とのことです。 in a recent test of top 50 UK retailer home pages (tested in Performance Analyser with Internet Explorer 11 at 8Mbps), the best Speed Index score was 819. The average was 3,658 (median 3,106), while the poorest had a score of 8,582 Speed Index – how it works and what it means ・Lighthouse Chrome Developer ToolsのAuditsにてパフォーマンス診断をするとLighthouseを使った分析が可能です。 実行結果としてSpeed Indexの値が表示されますが、目標の数値としては [< 1,250] となっています。 Chromeのソースコードには以下のようなコメントアウトの記述もあり、中央値を5,500と想定している模様です。 // 10th Percentile = 2,240 // 25th Percentile = 3,430 // Median = 5,500 // 75th Percentile = 8,820 // 95th Percentile = 17,400 参考URL: https://github.com/GoogleChrome/lighthouse/blob/v2.4.0/lighthouse-core/audits/speed-index-metric.js#L62 ・mediba運営のサービス 私たちが計測している複数のモバイルサイトのベンチマークは以下のようになりました。(2016年12月〜2017年8月) 計測概要 項目 内容 ツール WebpageTest 地点 EC2 north-west1 Viewport iPhone5c 帯域/RTT Mobile3GFast(1.6Mbps/768Kbps 150msRTT) 数値の算出方法 1日12回計測した上で月単位での中央値を取得 計測結果 計測対象 毎月の中央値の平均 A 3,452 B 4,276 C 4,808 D 5,330 E 5,756 F 6,098 ※D・Eは同期的なA/Bテストツールの読み込み(HTMLパースのブロッキング)があり、FはファーストビューにカルーセルUIが存在しているため、それを反映して数値が高くなっています。 感覚的にもレンダリングをブロックする要素がなければ(ボトルネックがなければ)、5,000以下の数値が出せる という印象です。 そしてLighthouseの5,500という中央値も肌感覚としては妥当に感じます。 そのため、medibaでは5,000を閾値としてパフォーマンス改善に取り組んでいます。 6,000を超えているような状況では「体感速度が遅く、改善の余地がある」といったざっくり判断をしています。 Speed Indexを取り入れるポイント 1. ざっくり捉える Speed Indexは描画の面積で算出するため、ファーストビューに自動送りのカルーセルが存在していたりバナー広告があったりする場合は不利になる事があります。 逆に画像の少ないテキストベースのデザインであれば有利になります。 ただ体感速度という意味ではある意味妥当ですし、他のベンチマークと比較はできないとしても同一計測対象での比較としては有用だと思います。 そのため、厳密さを求めずある程度の「ざっくり感」をもって取り入れると良いと思います。 2. 他の指標を使って改善する Speed Indexはあくまで結果としての数値(目的変数)なので、何かアクションを起こすためには、TTFBやfirstPaint、DOMContentLoadedなどの他の指標(説明変数)が必要になります。 またonLoadは古い指標と言われたりする事もありますが、onLoadが遅い場合はブラウザのインジケータの表示時間が増えるため、体感速度へのマイナス影響もあります。 そのため投機的な読み込みや不要なサードパーティ絡みのリクエストの削除など、onLoadの最適化も必要だと思います。 Speed Indexは他の指標を代替するわけではなく、相互的に補完していくイメージです。 3. ビジュアライズする Speed Indexの改善幅を数値で共有しても、なかなか相手に意図が伝わりづらいと思います。 そのため、数値(中央値)が近いもの同士の比較動画をWebPagetest上で作成し、数字と合わせて展開すると視覚的にもわかりやすく、コミュニケーションもスムーズになると思います。 速度改善のベネフィット パフォーマンス改善にかかるコストに対して、どういったベネフィットが期待できるかの説明に悩むことは常にあると思います。 「速い方がいい」のは間違いないですが、それだけでは協力を得る事が難しいです。 多くの人が納得できる説明が必要になるのですが、その一つとして「パフォーマンス改善は帯域の品質が低いほど効果が大きい」ということが挙げられると思っています。 最近のデータ契約は容量に上限(と超えた場合の通信制限)があることがほとんどです。 MVNOの市場シェアも伸びてきていますし、事業者によっては節約モード(下り制限)のスイッチを用意しているケースもあります。 つまり国内の通信環境にも多様性があり、ナローバンドも珍しくないので「通信速度が速いから大丈夫」というわけでもなさそうです。 というわけで、ナローバンドを考慮したときに、UXとしてのエンゲージメント貢献や事業指標への期待ができると思っています。 ※ rtt attribute がモバイルのChromeにも実装されたら、RUMとして収集するのも面白そうです。 おわりに Speed Indexを使ってボトルネックにフォーカスすることで、パフォーマンス改善をシンプルに考えることができると思います。 例えば、ボトルネックには以下のようなものが挙げられます。 CSSファイルの肥大化 同期的なScript読み込みによるHTMLパースのブロック 圧縮が有効になっていない 巨大なCSS Sprite画像の読み込み HTTP/1.1での同時接続数制限 キャッシュの設計の問題 どれも難しいことではなく解決のためのコストも低いですが、サードパーティのリソースも含めて、このいずれかが課題として見つかる事が多いです。 そしてどれもネットワーク(RTT)影響を大きく受けるため、RTT的に不利なモバイルネットワークではわかりやすく体感速度を上げることができました。 Speed Indexは精度が高くないかもしれませんが、[体感速度が遅いかどうか]を把握し次のアクションに繋げる事ができるため、より良いモバイルUXを提供できる指標だと思っています。
アバター
こんにちは、AWS Lambda と戯れる日々を過ごしているインフラストラクチャー部の沼沢です。 みなさん、AWS Lambda 使っていますか? Lambda では CloudWatch Logs に /aws/lambda/関数名 というロググループ名でログを出力することができますね。 Lambda@Edge でも同じようにログを出力しようと思ったのですが、ロググループが見つからず困ったので同じように困ってる人の助けになれば幸いです。 結論 以下の公式ドキュメントを見るとちゃんと書いてあります。 Lambda 関数の CloudWatch メトリクスと CloudWatch Logs - Amazon CloudFront 以下引用です。 Lambda は、関数が実行される場所に最も近い CloudWatch Logs リージョンで CloudWatch Logs ログストリームを作成します。各ログストリームの名前の形式は、/aws/lambda/us-east-1.function-name です。 つまり、日本で CloudFront にアクセスして Lambda@Edge を動作させた場合は東京リージョン近辺のエッジロケーションにアクセスされるので、東京リージョンの CloudWatch Logs に出力されます。 Lambda@Edge では関数自体はバージニアリージョンに作成する必要があるため、てっきりログもバージニアリージョンの CloudWatch Logs に出力されるもとの思い込んでいました。 そのため、最初は Lambda@Edge ではログが出せないのかと思ってしまいましたが、無事に発見することができました。 困ってからドキュメントを見るという癖が付いているため、少しハマったというお話でした。 公式ドキュメントはちゃんと見ましょう。(自戒)
アバター
こんにちは、インフラストラクチャー部の沼沢です。 以前、 Lambda@Edge を使ってデバイス判定をする記事を書きましたが、最近 Lambda@Edge が正式リリースされたので、正式版での検証も実施してみます。 以前書いた記事はこちら Lambda@Edge でデバイス判定をする | mediba Creator × Engineer Blog 概要 今回も、前回と同じように以下の判定をできるようにします。 iPhone iPad Android 上記以外(Other) CloudFront の設定も前回と同じく、 Viewer Request に Lambda@Edge を定義します。 やってみる オリジンサーバー相当の EC2(nginx) を用意 前回と同様の手順なので割愛します。 nginx のアクセスログをカスタマイズ こちらも前回と同様の手順なので割愛します。 ただし、今回は “X-Custom-Device” というヘッダー名にしているためそこだけ変更しましょう。 Lambda ファンクションを用意 Lambda@Edge のファンクション作成についてはこちらの公式ドキュメントが参考になります。 AWS Lambda@Edge - AWS Lambda Preview の時は東京リージョンで作っても動作しましたが、正式版の Lambda@Edge は、 バージニアリージョンで作成する必要があるため、必ずバージニアリージョン(us-east-1)で作成しましょう。 Lambda 関数の作成 設計図の選択: ブランク関数 トリガーの設定: (何も変更せず) 次へ 関数の設定 名前: device_judge_test 説明: device judge ランタイム: Node.js 6.10 (←ここはPreview時は Edge Node.js 4.3 でした) Lambda 関数のコード コード エントリ タイプ: コードをインラインで編集 以下のコードを入力 'use strict'; exports.handler = (event, context, callback) => { const customHeaderName = 'X-Custom-Device'; const uaHeaderName = 'User-Agent'; const request = event.Records[0].cf.request; const headers = request.headers; if (headers[uaHeaderName.toLowerCase()]) { const device = { "key": customHeaderName, "value": headers[uaHeaderName.toLowerCase()][0]['value'].match(/(Android|iPhone|iPad)/)? RegExp.$1: 'Other' }; headers[customHeaderName.toLowerCase()] = [ device ]; } callback(null, request); }; Lambda 関数ハンドラおよびロール ハンドラ: index.handler ロール: テンプレートから新しいロールを作成 ロール名: lambda_edge_execute_role ポリシーテンプレート: 基本的な エッジ Lambda のアクセス権限 関数のテストは、サンプルイベントテンプレートの “CloudFront AB Test” を選択して、User−Agent の値だけ書き換えて実施すると良い 作成完了後、以下の手順で Lambda 関数の新しいバージョンを発行 CloudFront Web Distribution を用意 以下の通り設定していきます。 作成後、Status が Deployed になるまで待ち、CloudFront の Domain Name (xxxx.cloudfront.net) にアクセスした際に nginx のデフォルトページが表示されれば準備は OK です。 動作検証 今回の検証で期待する動作は、「X-Custom-Device が同じリクエストは、30 秒間(Age が 30 になるまで)はオリジン側の nginx にアクセスは来ず、CloudFront がキャッシュを返すこと」です。 これを確認するため、以下のコマンドを流して確認します。(MacOS 向け) ua_list=("{判定させたいUA文字列1}" "{判定させたいUA文字列2}" "{判定させたいUA文字列3}" "{判定させたいUA文字列4}" ...) i=0 v=0 while : do curl -i -s -H "User-Agent:${ua_list[$v]} ${i}" http://**************.cloudfront.net/ | egrep "^(HTTP|X-Cache|Age)" echo "" sleep 0.85 i=$(( i + 1 )) if [ $v == `expr ${#ua_list[*]} - 1` ]; then v=0 else v=$(( v + 1 )) fi done このコマンドに各 User-Agent を設定してアクセスし、nginx のアクセスログを確認します。 インクリメントした数字を最後に付与しているのは、User-Agent が変わってもデバイス判定結果( X-Custom-Device )が同じ場合にはオリジンにアクセスが来ないことを確認するためです。 “その他” 判定 “その他” を判定させるため、Google Chrome の User-Agent (以下)でアクセスしてみます。 User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 コマンド実行結果は以下。 HTTP/1.1 200 OK X-Cache: Miss from cloudfront ← 初回アクセスなので Miss HTTP/1.1 200 OK Age: 1 X-Cache: Hit from cloudfront ← 2回目以降は User-Agent が異なっても iPhone 判定なので Hit 〜〜〜中略〜〜〜 HTTP/1.1 200 OK Age: 29 X-Cache: Hit from cloudfront ← Age: 29 までは Hit HTTP/1.1 200 OK X-Cache: RefreshHit from cloudfront ← Age: 30 を迎える頃に RefreshHit HTTP/1.1 200 OK Age: 1 X-Cache: Hit from cloudfront ← RefreshHit 以降はまた Age:30 になるまで Hit 記録された nginx アクセスログは以下。 ***.***.***.*** - - [07/Aug/2017:16:25:02 +0900] "GET / HTTP/1.1" 200 3770 "-" "Amazon CloudFront" "***.***.***.***" Other ***.***.***.*** - - [07/Aug/2017:16:25:32 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" Other 無事に末尾に “Other” が記録され、最初に “Other” 判定されたアクセスから30秒間は、"Other" 判定される他のアクセスは nginx 側には来ませんでした。 “iPhone” 判定 “iPhone” を判定させるため、以下の 4 つのバージョンの User-Agent で順番にアクセスを繰り返してみます。 iOS 10 Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F89 Safari/602.1 iOS 9 Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_5 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13G36 Safari/601.1 iOS 8 Mozilla /5.0 (iPhone; CPU iPhone OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4 iOS 7 Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53 コマンド実行結果は “その他” 時と同様なので割愛します。 記録された nginx アクセスログは以下。 ***.***.***.*** - - [07/Aug/2017:17:33:36 +0900] "GET / HTTP/1.1" 200 3770 "-" "Amazon CloudFront" "***.***.***.***" iPhone ***.***.***.*** - - [07/Aug/2017:17:34:06 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" iPhone 無事に末尾に “iPhone” が記録され、最初に “iPhone” 判定されたアクセスから30秒間は、"iPhone" 判定される他のアクセスは nginx 側には来ませんでした。 “iPad” 判定 “iPad” を判定させるため、以下の 4 つのバージョンの User-Agent で順番にアクセスを繰り返してみます。 iOS 10 Mozilla/5.0 (iPad; CPU OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.0 Mobile/14F91 Safari/602.1 iOS 9 Mozilla/5.0 (iPad; CPU OS 9_3_5 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13G36 Safari/601.1 iOS 8 Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4 iOS 7 Mozilla/5.0 (iPad; CPU OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53 コマンド実行結果は “その他” 時と同様なので割愛します。 記録された nginx アクセスログは以下。 ***.***.***.*** - - [07/Aug/2017:17:58:10 +0900] "GET / HTTP/1.1" 200 3770 "-" "Amazon CloudFront" "***.***.***.***" iPad ***.***.***.*** - - [07/Aug/2017:17:58:40 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" iPad 無事に末尾に “iPad” が記録され、最初に “iPad” 判定されたアクセスから30秒間は、"iPad" 判定される他のアクセスは nginx 側には来ませんでした。 “Android” 判定 “Android” を判定させるため、以下の 4 つのバージョンの User-Agent で順番にアクセスを繰り返してみます。 Android 7 Mozilla/5.0 (Linux; Android 7.0; SCV36 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36 Android 6 Mozilla/5.0 (Linux; Android 6.0.1; SOV34 Build/39.0.C.0.282) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Mobile Safari/537.36 Android 5 Mozilla/5.0 (Linux; Android 5.1.1; SOV32 Build/32.0.D.0.282) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36 Android 4 Mozilla/5.0 (Linux; Android 4.4.4; SOL26 Build/23.0.C.0.296) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Mobile Safari/537.36 Android 2 Mozilla/5.0 (Linux; U; Android 2.3.5; ja-jp; IS12F Build/FGK600) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 コマンド実行結果は “その他” 時と同様なので割愛します。 記録された nginx アクセスログは以下。 ***.***.***.*** - - [07/Aug/2017:18:11:28 +0900] "GET / HTTP/1.1" 200 3770 "-" "Amazon CloudFront" "***.***.***.***" Android ***.***.***.*** - - [07/Aug/2017:18:11:58 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" Android 無事に末尾に “Android” が記録され、最初に “Android” 判定されたアクセスから30秒間は、"Android" 判定される他のアクセスは nginx 側には来ませんでした。 あるデバイスのキャッシュがある状態で別デバイス判定のアクセスをする 例えば “iPhone” のキャッシュがある状態で “Android” のアクセスをした場合に、"iPhone" のキャッシュを返さず、オリジンにアクセスが来るか、を検証します。 “iPhone” 判定のコマンドを先に開始し、15秒後に “Android” 判定のコマンドを開始して検証。 記録された nginx アクセスログは以下。 ***.***.***.*** - - [07/Aug/2017:18:22:00 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" iPhone ***.***.***.*** - - [07/Aug/2017:18:22:15 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" Android ***.***.***.*** - - [07/Aug/2017:18:22:30 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" iPhone ***.***.***.*** - - [07/Aug/2017:18:22:45 +0900] "GET / HTTP/1.1" 304 0 "-" "Amazon CloudFront" "***.***.***.***" Android 15秒後に “Android” 判定のアクセスがあり、それぞれが30秒ごとにオリジンにアクセスが来ることが確認できました。 あとがき 今回行った CloudFront の設定上は、適当にクエリパラメータを付けたり、Cookie を変更したりしても、1つの Path に対してはデバイス判定結果( X-Custom-Device ) が異ならない限り同じキャッシュが返ります。 これらの検証結果までは載せていませんが、このことを意識すると、オリジンで判定処理等をすること無く CloudFront の Edge 上でキャッシュの単位をコントロールできる、と考えられます。 ただし、本記事執筆時点で Lambda@Edge は 1秒 or 3秒のタイムアウト(変更不可) があるため、あまり大変な処理はできないので、なるべく簡単な処理に留めることが大事です。 ちなみに、今回のコードではおおよそ1ミリ秒以内で処理できました。 参考: Lambda@Edge の制限 また、今回の検証では X-Custom-Device ごとのレスポンス出し分けまではしませんでしたが、この仕組みを使えば User-Agent を通さずにデバイスごとにレスポンスを出し分けられるため、キャッシュヒット率の向上=オリジンサーバーの負荷軽減、レスポンス速度改善が見込めます。 Lambda@Edge は無事に正式リリースされたので、mediba でもどんどん導入していく予定です。
アバター