TRILL Tech Blog

国内最大級の女性向けメディアTRILLの開発ブログです。

2段階認証 (TOTP) をSwiftで実装してみる

TRILL開発部の石田です。

2段階認証の設定で、QRコードを読み込んで30秒ごとに変わる6桁の数字が生成される、という仕組みをよく見かけます。 今回はSwiftでその2段階認証の仕組みを実装してみました。

2段階認証 (TOTP) とは

2段階認証にはいくつか種類があり、SMS認証やGoogle Authenticatorのようなアプリ認証、YubiKeyのようなデバイス認証の3種類が一般的です。

この記事ではGoogle Authenticatorのようなアプリ認証を実際にiOSアプリとしてSwiftで実装し、中身を紐解いていきます。

Google Authenticatorのようなアプリ認証をTOTP (Time-based One-Time Password) と言うのですが、仕組みとしてはQRコードで16文字の英数字を読み取り、その英数字と現在時刻を使って6桁の数字を生成します。 外観は以下の図のようになります。

f:id:trill_tech:20210408175242p:plain

16文字の英数字と書きましたが、正確にはBase32文字列であり、自由に設定可能な文字列ではありません。

TOTPの仕組み

2段階認証用のQRコードを解析すると以下のようなURLになっています。

otpauth://totp/Twitter:@username?secret=SH2BZBC3H7DISN6Z&issuer=Twitter

QRコード形式の場合は、QRコードと16文字の英数字 (秘密鍵) が表示されるのですが、URLのsecretにあたる部分が秘密鍵となります。 この秘密鍵を使ってワンタイムパスワードを生成します。

具体的には以下のようにパスワードを生成します。

  1. UnixTimeを30で割った値 (小数点切り捨て) をCounterとする
  2. 秘密鍵とCounterを使ってHMAC-SHA-1で20byteのハッシュ値を計算する
  3. ハッシュ値の下位4bitから符号なし整数Offset (0〜15) を生成する
  4. ハッシュ値20byteのうち、Offset番目のbyteから4byteを取り出す
  5. 4byteの上位1bitを取り除いたデータを数値にする
  6. 得られた数値のうち下位6桁がワンタイムパスワードとなる

ちなみに、パスワード生成の過程でハッシュ化やデータの切り取りを行っているので、6桁の数字から秘密鍵を逆算することは不可能です。

TOTPをSwiftで実装してみる

実装にあたり、CryptoSwiftBase32 を利用しました。

TOTPのコードは以下のようになります。

import Base32
import CryptoSwift

// Base32に変換
guard let key = base32Decode("XXXXXXXXXXXXXXXX") else { return }

// 1. 現在時刻からカウンターを生成
let unixTime = Int(Date().timeIntervalSince1970)
let timeSteps = unixTime / 30
// 8byteのデータに変換
let counter = withUnsafeBytes(of: timeSteps.bigEndian, Array.init)

// 2. HMAC-SHA-1でハッシュを生成
let hash = try! HMAC(key: key, variant: .sha1).authenticate(counter)

// 3. hashの下位4bitを整数に変換
let offset = Int(hash.last! & 0b00001111)

// 4. hashのoffset番目のバイトから4バイト取得
var slicedHash = Array(hash[offset ... offset + 3])

// 5. 4byteの上位1bitを取り除いたデータを数値にする
slicedHash[0] = slicedHash[0] & 0b01111111
let num = Data(slicedHash).withUnsafeBytes { $0.load(as: UInt32.self).bigEndian }

// 6. 下位6桁を取得
let totp = String(num).suffix(6)

print(totp)

普通のiOSアプリの実装ではあまり見かけない文法がありますが、文字列に秘密鍵を入力すると、QRコードを読み込んだ2段階認証のアプリと同じ値となると思います。

まとめ

Swiftで2段階認証 (TOTP) を実装しました。

普段何気なく使っている2段階認証ですが、中身を紐解くと色々と気付きがあり、またSwiftでのビット演算など普段あまり使わない文法も知れて勉強になりました。

delyでは全方面でエンジニアを積極採用中です。 興味のある方は是非お声がけください。

dely.jp

ベイズの定理をできるだけ分かりやすく紹介してみる

TRILL開発部の石田です。

先日Twitterで以下のツイートが流れてきました。

ある検査は精度95%で正しい結果を出すが、5%の確率で間違えた結果を出す。 実際に病気である人は全体の5%だ。

その検査で陽性反応がでた。この人が本当に病気である確率は何%か?

非常に興味深い内容なので、こちらについて言及していきたいと思います。

ちなみに元ツイートを参照すると分かりますが、正解は95%ではなく50%です。

問題の整理

まず、ツイート中の以下の文について考えてみます。

ある検査は精度95%で正しい結果を出すが、5%の確率で間違えた結果を出す。

これは言い換えると以下のようになります。

  • 病気の人が検査をしたとき、陽性と出る確率が95%で、陰性と出る確率が5%
  • 健康な人が検査をしたとき、陽性と出る確率が5%で、陰性と出る確率が95%

また、問いは以下となっています。

その検査で陽性反応がでた。この人が本当に病気である確率は何%か?

これも言い換えると以下のようになります。

  • 陽性と出たとき、病気である確率は何%か

このツイートは一見当たり前のことを問うているようなのですが整理すると、

  • 与えられた確率: 病気 or 健康な人が検査をしたときの結果の確率
  • 求めたい確率: 検査結果が陽性のときの病気の確率

となり、与えられた確率と求めたい確率が違うことが分かります。

例を挙げると、仮に検査結果が病気でも健康でも全て陽性と出るとき、「検査結果が陽性のときの病気の確率」は100%でも0%でもなく、実際に病気である人の割合と同じになることが分かると思います。

ベイズの定理

さて、上記の問いを解く前にベイズの定理について説明します。

まず条件付き確率について説明します。

条件付き確率とは「Aが起こったときにBも起こる確率」であり、例えば「病気のときに陽性と出る確率」のようなものです。 「Aが起こったときにBも起こる確率」は P(B | A) と表現されます。

P(B | A) は、Aが起こった確率 P(A) のうちで、AとBが起こる確率 P(A \cap B) なので、

\begin{equation} P(B | A) = \frac{P(A \cap B)}{P(A)} \end{equation}

と表現できます。

ちょっと分かりづらいので具体例を挙げてみます。

サイコロを振って「偶数が出たとき出目が4以上である確率」を考えると、確率は P(4以上 | 偶数) と表され、偶数が [2, 4, 6] でありその中で4以上なのは [4, 6] となるので \frac{2}{3} と求められます。

同様に出目が偶数の確率 P(偶数) は [2, 4, 6] の \frac{3}{6} = \frac{1}{2} で、偶数かつ4以上の確率 P(偶数 \cap 4以上) は [4, 6] の \frac{2}{6} = \frac{1}{3} となります。

式に当てはめてみると、

\begin{align} P(4以上 | 偶数) &= \frac{P(偶数 \cap 4以上)}{P(偶数)}\\ &= \frac{\frac{1}{3}}{\frac{1}{2}}\\ &= \frac{2}{3} \end{align}

となり、等式が成り立つことが分かります。

P(B | A) と同様に P(A | B)

\begin{equation} P(A | B) = \frac{P(A \cap B)}{P(B)} \end{equation}

と表現でき、2つの式の分母を払って P(A \cap B) を削除する等式を書くと、

\begin{equation} P(B | A)P(A) = P(A | B)P(B) \end{equation}

となり変形すると、

\begin{equation} P(B | A) = \frac{P(A | B) P(B)}{P(A)} \end{equation}

という式になります。

これがベイズの定理です。

問題の解法

ベイズの定理だけでは何のことかよく分からないので、ベイズの定理を使って実際に先のツイートの問題を解いてみます。

求めたい確率は「陽性と出たとき、病気である確率」でした。 これにベイズの定理を当てはめると、

\begin{equation} P(病気 | 陽性) = \frac{P(陽性 | 病気) P(病気)}{P(陽性)} \end{equation}

となります。

値を埋めていきます。

実際に病気である人は全体の5%だ。

とあるので、  P(病気) = 0.05 となり、病気の人が検査をしたとき陽性と出る確率が95%なので  P(陽性 | 病気) = 0.95 となります。

陽性の人の確率  P(陽性) はちょっとややこしいのですが、陽性の人が全体の何%か考えると、病気の人 (5%) の中で陽性と出た人 (5% x 95%) と、健康な人 (95%) の中で陽性と出た人 (95% x 5%) の合計なので、

\begin{align} P(陽性) &= 0.05 \times 0.95 + 0.95 \times 0.05\\ &= 0.095 \end{align}

となります。

以上より、ベイズの定理を使って  P(病気 | 陽性) を求めると、

\begin{align} P(病気 | 陽性) &= \frac{P(陽性 | 病気) P(病気)}{P(陽性)} \\ &= \frac{0.95 \times 0.05}{0.095} \\ &= 0.5 \end{align}

と計算でき、50%となります。

まとめ

ベイズの定理を具体例を交えて紹介しました。

分かったようで分からない感のあるものだと思っており、自分もよく混乱しています。

この記事を読んで少しでも理解の一助となれば幸甚です。

delyでは全方面でエンジニアを積極採用中です。 興味のある方は是非お声がけください。

join-us.dely.jp

デザインの指示に迷った時は、 「要素に分解」がいいかもという話

こんにちは。
TRILL開発部で、webプロダクトのPO兼デザイン周りも担当しています、
yuaoです。

この記事は「dely #2 Advent Calendar 2020」21日目の記事です。

adventar.org

dely #1もありますので、こちらも是非ご覧ください。

adventar.org


昨日は akina.mさんの記事
「システム管理者に贈る「運用改善に役立った!」AWSの機能4選」でした!

tech.dely.jp

akina.mさんにはTRILLでもとってもお世話になっています。
私にとってシステムの領域はまだまだまだまだ未知の部分が多いのですが、
システム管理者さんの視点で書かれていて、とても勉強になりました。
akina.mさんいつも本当にありがとうございます!

さて、初めてこういったものを書かせていただきますが、
今回は、
デザインをデザイナーさんにお願いしたら
ちょっとイメージとちがうものが上がってきた、
そんな時に、どんな風に修正指示を考えるか…

というのをまとめてみました。

尚、今回指している「デザイン」とは
主に表層デザイン、ビジュアルデザインのことを扱っています。

どうしているか

結論から言いますと、タイトルの通りでして
デザインを要素(「色」「線」「形」などのこと)に分解して、
どこに違和感があるか、を
なるべく具体的に伝えるようにしています。

修正指示の話をする前に

まずは大前提として、制作をお願いする時は、
デザイナーさんとしっかり情報の認識合わせをする
というのが必須です。
上がってきたデザインが、イメージに合わせて貰えているかどうかは、
オーダー時の情報共有に、抜け漏れ無いようにする必要があります。

デザインで何を伝えたいのか、
どう言った用途で使うのか
納期、画像サイズなどの規約
この辺りは基本です。

そして、TRILLに関するデザインを作成して貰う場合で言うと、
もう1つ大事な共有事項があって、
それがブランドイメージです。

TRILLではブランドガイドラインなるものがあり、
ブランドを表現するための、考え方や、
具体的なデザインルールなどがまとまっています。
例えば色やフォント、画像の加工方法など、
細かく言語化されています。

f:id:trill_tech:20201218194146j:plain

なので、新しくデザイン制作に関わっていただくデザイナーさんには
最初に展開する、大事な資料の1つとしています。
(補足として、ブランドガイドラインは全てそのルールに則って作れ!ということではなく、 世界観を統一する為の推奨であり、 それを踏まえて考えてね、という感じのものです)

デザインサンプルを見せてもらう

前置きが長くなりましたが、ここからが本題です。
デザインをお願いする時の情報は揃えた。
認識合わせもした。
そして制作を進めて頂いて、さあサンプル確認だ。

「…あれ?」
ってなることがあります。

勿論、イメージ通りのものを上げて頂く事多々あります。
が、「うーん?」となることもある。。
つまり違和感を感じる。

違和感を感じるものをOKには出来ないので、
ここから修正をお願いする事になります。
そこで「どこをどんな風に修正してもらうか」
を考えなければなりません。

私個人的に…ですが、
デザインにおける違和感を「ダサい」という一言で言わないようにしています。
その言葉は、その人の「好み」が根底にある場合が多く
ロジカルな指摘ではないと思っているので…
(メンバーの皆様、うっかり口を滑らせてたらほんとすみません)


自分が感じる違和感がどこからくるのかを突き止める

上がってきたデザインに違和感を感じたら、
その違和感を言語化することが、
とっても大事だと思っています。

そして違和感を言語化する時に、
私が頭の中に引っ張り出すのが
先述しました「デザインの要素」です。
グラフィックになる部分というのは、要素に分けることができます。

デザインの要素
  • テクスチャ
  • 空間
  • フォーム
  • フォント


こちらのタカハマ ケンタさんの記事がめっちゃ分かり易くて、
個人的には何度も読み直して、
とても参考にさせていただいています。

感覚派デザイナーも知っておいて損はない「デザインの要素と原則」 webnaut.jp

表層デザインやグラフィックデザインは、
大前提として「伝えるべき事」という骨格があって
それをいかに「的確に」伝えるかを司る部分です。

その「的確」の中には、サービスのブランドイメージも勿論含まれているので、
TRILLの表層デザインやグラフィックデザインを作るときというのは即ち、
「TRILLとして、情報を伝える」為に、
厳選したデザイン要素を用いて、構成すること
を言います。

違和感の分解の仕方例

(あくまで私の中のプロセスなので一例として)

ではデザインを見た時に感じた「違和感」はどこからくるのか。

「ブランドイメージに合っていないのかな?」
「伝えなければいけないことに合っていないのかな?」
まず大枠で分けるとこんな感じになる事が多いです。

そして、
それなら「合ってない」と感じるのは
どの要素が影響しているんだろう?
みたいな考え方をしていきます。


例えばこの記事のタイトルサムネイルのデザインが
こんな感じで上がってきたら。

f:id:trill_tech:20201218183015j:plain

※シンプルに説明する為要素限定しています。
実際はもっと複合的に使う場合もあり(フォントと色とテクスチャ、背景画像、装飾など) より複雑なことが多いです。


違和感を感じるところはざっとこんな感じ
(今回はTRILLブランドがどうかというのは省きます)

【大枠】
伝えなければいけない事を、スムーズに伝えられない可能性がある

何故か↓

【フォント
フォントサイズは的確か。読み始めの「デザインの」の部分に、視点が最初にいかないのではないか。
【色】
テキストに使っている色にはどういう意図があるのか。意図した強弱になっているか。
【フォント】
選択しているフォントは的確か。途中でフォントを変更する意図は何か。こちらも読み始めの部分に視点を持っていけないのではないか。
【空間】
文字間は的確か。「がいいかも」「とい」「う話」で分かれて読めることで、読み手のリズムを崩さないか
【空間】
文言全体が画面に対して左に寄っていないか。左右の余白を変える意図は何か。重心が偏ることで、バランスが悪くなっていないか。

こんな感じで違和感を要素毎に分けて、
作り手の意図を聞いたり、修正の提案をしたりするのに使っていきます。

まとめ

表層デザインやグラフィックデザインは、
それが好きか嫌いかみたいな感情に先に触れることもあり、
違和感を言語化するのが難しい部分だなと思います。

私自身デザインを作ることもあるので、
自分が指示を受ける側だとしたら
具体的に言ってもらえた方が修正し易く、
効率的に対応できるなと思うので、
こんな進め方をするようになりました。

要素に分解していくと、
フラットに、気になる部分が見えてきて、
改善を考えるきっかけにし易くなりますし、
先ほどご紹介した記事に掲載されている他の「原則」なども併せていくと、
より、いろんな応用ができるので、
何かの参考になれば幸いです!

尚、要素分解は自分でデザインを作る時にも有効で、
自分が扱ってる要素は、
芯にある情報を、的確に伝えるためのものに出来ているかな?
と、デザインを俯瞰して見る時に使えたりするので、
個人的にはオススメです。

おわりに

リップサービスも含まれていると重々承知していますが、
一緒にお仕事をしている他のデザイナーさんに、
指示が分かり易いと言って頂いたことがあったので、
こんな感じで記事にまとめてみようと思い至りました。
デザイナーさん達、本当にいつもありがとうございます!

プロダクトはあらゆる分野のものが複合的に重り
1つのものになっていて、
表層・ビジュアルデザインはその一部分です。
delyの中には、その各分野を解像度高く見れるメンバーがたくさんいて、
それぞれがより良いパフォーマンスをすべく、日々走っています。

私も自分の担当領域でのパフォーマンスを上げていくべく、
デザイン(今回は表層・グラフィックだけでなく、情報設計や体験設計も含めた意味)
プロジェクト推進の為に、これからも精進していきたいなと思います。

積極募集中

delyでは一緒にサービス成長させるエンジニアさんや
デザイナーさんを積極採用中です。
興味のある方はぜひカジュアルにお話しさせてください!

join-us.dely.jp


また、delyではTechTalk という社内のメンバーがテーマ毎に話すイベントも開催していますので、
こちらもぜひチェックしてみてください!

bethesun.connpass.com

明日は、クラシルのUIデザイナーkassyさんによる
「2020年 UIデザイナーが読んで良かった本 9冊」です!
私自身、読まなきゃと思っている本が溜まっているのですが、
ご紹介されているものが興味深いものが多く、
さらに積まれそうな予感がします。。
是非こちらもご覧ください!

Google Optimizeでテストをしてる話

ごきげんよう!

TRILL開発部のWebを担当しています、maseoです。

この記事は「dely #2 Advent Calendar 2020」19日目の記事です。

dely #1 Advent Calendar 2020 - Adventar adventar.org

dely #2 Advent Calendar 2020 - Adventar adventar.org


昨日は HPdM(ハイパー プロダクト マネージャー)のRiceさんの記事「初心者PdMに贈る「"伝書鳩"が意思を持つために意識すべきこと」でした。
エンジニアはHPdMさんに本当に助けてもらってます。
こんなに色々考えてくださっているのは本当にありがたいです。いつもありがとうHPdMさん達!


さて

今回はTRILLのWebで行っているGoogle Optimize*1を使ったA/Bテストはこんな感じ!というお話を書きたいと思います。

TRILLでは、以下のドキュメントを参考に、Google Optimize JavaScript API を利用しています。

developers.google.com


TRILL Webでこの方法を採用している理由は色々ありますが、A/Bテストを行う上でサーバーサイド側の操作がGoogle Optimize上では厳しいとか、テスト内容によってはDBから取得する値を変えたいとか、テスト用の広告を入れたいとかです。


もう少し具体的に言うと、
記事詳細でA/Bテストがしたい!となった時に、URLが記事のID毎に異なっているため、「このタイプの記事はテスト対象……、このタイプの記事は非対称……」なんてことがGoogle Optimizeのビジュアルエディタでは大変そうだな……ってなったからです。

実装

今回は過去に実際に実施したテストを書いてみます。
※仮説の設定とか、テストパターンの作成とかは割愛します。

■テスト内容
新しいモジュールを追加するにあたって、
どのタイプの表示がいいかな〜?そもそも追加しても他のモジュールに影響はないかな〜?
を確認したい

■テストパターン

  1. オリジナル(現行パターン)
  2. サムネイル小
  3. サムネイル大

の3パターン

ざっくりこんな感じです。

それでは、早速ですが、実装コードはこちら(ドンっ

export default class OptimizeUtil {
 constructor() {
     this.getElement();
  }

 init() {
  gtag('event', 'optimize.callback', {
   callback: (value) => {
    this.callback(value);
   }
  });
 }

 getElement() {
  this.nextArticleList = document.querySelector('.js-next-article-list');
  this.nextArticleItems = document.querySelectorAll('.js-next-article-item'); 
  …
 }

 changeClassToSmall3() {  // テストパターンの表示を実現する操作
  if (this.nextArticleList) {
   this.nextArticleList.classList.remove(`Articles_List`);
  }

  if (this.nextArticleItems) {
   this.nextArticleItems.forEach((el) => {
    el.classList.remove(`Articles_Item`);
    el.classList.add(`Articles_Item-small3`);
   });
  }
       …
 }

 changeClassToBig3() {  // テストパターンの表示を実現する操作
  …
 }

 callback(value) {
  switch (value) {
   case '0':
    break;

   case '1':
    this.changeClassToSmall3();
    break;

   case '2':
    this.changeClassToBig3();
    break;

   default:
    break;
  }
 }
}


一部割愛してますが作りはシンプルです。
テストパターン毎に適切な関数を呼び出して、あとはCSSとかで見た目を整えてあげます。

このテスト方法では、Google Optimizeでそれぞれのパターンの効果が見られるのはもちろん、新しいクリックイベントの計測もできますし、 今回は実装されていませんがパターン毎にリンクにクエリパラメータを簡単に付けれたりします。

例えば、Adjustを使ったアプリストアへの送客なんかもパターン毎の計測が簡単です。


また、同じ "記事" だけど、Aカテゴリーの記事ではテストして、Bカテゴリーの記事でテストしない!みたいなこともHTMLの出しわけとかで簡単に制御できます。(トラフィックには影響ない範囲で)

全部まとめて実装できるのも良いですね。レビューなども普段のフローでできるので、安心です。

表示確認

そして見た目はこんな感じになります。(ドドンっ

f:id:trill_tech:20201215193353p:plain
表示結果1

f:id:trill_tech:20201215193428p:plain
表示結果2

ちゃんと表示が変わっていますね!

あとはテストを見守るのみです。
他のモジュールに悪い影響がないかに注意して日々を過ごします。

テスト終了

時は流れ、
オプティマイズの結果はこんな感じになりました。

f:id:trill_tech:20201217171503p:plain
オプティマイズの結果

差が出てますね。(よかった)
計測データで他のモジュールにもマイナスな影響は確認されなかったので、テストしたパターンは無事採用されました!(よかった)


ただ、このテストはGoogle Optimize上では全ユーザーに出てることになっておりまして、
Optimizeのトラフィックの割り当てを使ってないです。(別途ゴニョゴニョして数%のユーザーをテスト対象としています)
なので、トラフィック割り当てを使う場合は、 テストパターンの対象になっていないユーザーのこともしっかり考える必要があります。


まとめ

ということで、複雑なタイプのABテストも実施できるようになりました。

エンジニアの実装コストなどの問題はありますが、今のところはいい感じに運用できています。

今後もより良いテスト環境が作れるように、そしてしっかりPDCA回せるように頑張ります。

おわりに

明日は akina.m さんの「システム管理者に贈る 運用改善に役立った!AWSの機能4選」です!お楽しみに!( \\ 楽しみ!! // )

delyではエンジニアを積極採用中です。ご興味がありましたら、是非お気軽にお話させてください! join-us.dely.jp

また、delyについて詳しく知りたいよって方は、TechTalk という社内のメンバーがテーマ毎に話すイベントもあるのでこちらも是非お気軽にご参加ください! bethesun.connpass.com

*1:Googleが提供している、A/Bテストを行うツール

初心者PdMに贈る「"伝書鳩"が意思を持つために意識すべきこと」

f:id:trill_tech:20201216191023p:plain

こんにちは! TRILL開発部PdMの米田(@rice_ynd)です。

この記事は「dely #2 Advent Calendar 2020」18日目の記事です。

昨日はTRILL Android担当 永井さんの記事「Merged Manifest を使って uses-permission を調査した話」でした。

dely #1 Advent Calendar 2020 - Adventar adventar.org

dely #2 Advent Calendar 2020 - Adventar adventar.org

さて今回の記事ですが、プロダクト開発においてPdMが果たすべき役割について書いてみたいと思います。

PdMに任命されたけど、何をしたらいいの?」な人や「言われたことを言われたとおりにしかできない。やばい。」な人にぜひ読んでみてほしいです。

そもそもPdMって?

一口にPdMといっても、プロダクトやチームの規模やフェーズ感、そのチームが担う責務によって細かく役割は異なるかと思います。

ただどんなプロダクトやチームにおいても本質は変わらないと考えていて、「プロダクトをより良くするために、方針を指し示し舵を切ること」がPdMの役割だと思っています。

いまチームがどこを向くべきで、そのために何が必要かをメンバーに明示し、それをマネジメントすることができている状態が、PdMとして正しい役割を果たせている状態だと言えると思います。

そもそもPdMがどうあるべきかという包括的な話は、弊社で新規事業開発をしている奥原さん(@okutaku0507)の記事「プロダクトマネージャー1年目の教科書」が参考になりますので、興味がある方はぜひ読んでみてください。 note.com

"伝書鳩"になっている状態

では、どのような状態がPdMとして正しい役割を果たせていない状態なのでしょうか。

PdMに限らず、チームを推進する立場にある人が陥りがちなのが"伝書鳩"になってしまう状態です。
これはわかりやすく、PdMとして正しく機能していない状態であると思います。

具体的にどういうことかというと、例えばPdMにあたる役割の人が

  • 他部署や他チームからの依頼や相談をそのまま自チームのメンバーに伝えている
  • 経営層や上長の提言をそのまま方針としてチームに指し示している
  • 物事の優先度が"言われた順"になってしまっている

に当てはまる状態は、怪しいです。

これらが具体的にどう正しくないのか、ひとつずつ見ていきます。

◯ 他部署や他チームからの依頼や相談をそのまま自チームのメンバーに伝えている

これ自体が悪いというわけではありませんが、依頼の意図や相談によって解決したいことなど、本質を理解せずにチームに落とすだけではPdMを介する意味がありません。

むしろフローがひとつ増える分、コストが増えてしまいます。

その依頼を遂行すること or 相談を解決することで

  • 何が改善するのか
  • どんな価値が生まれるのか
  • どんな負が生まれるのか

などを理解し、優先順位を決定したり、足りない情報を補完したり、時に別の提案を返したりと、プロダクト開発をスムーズに推進するための付加価値を生むことがPdMの役割として適切です。

◯ 経営層や上長の提言をそのまま方針としてチームに指し示している

前述の項目とほぼ同様ですが、意思を持たないPdMはチームに必要ありません。

他者の意見やアドバイスを咀嚼し、それがプロダクトにとって本当に必要かどうか、有効な打ち手かどうかを見極め、施策に取り入れるなどの判断をするのがPdMの役目です。

◯ 物事の優先度が"言われた順"になってしまっている

「プロダクトをより良くする」ためには、数多ある問題の中からインパクトの大きいものを課題化し改善していく必要があります。
"インパクトの大きいもの"を測る観点として重要なのは、事業KPIに与える影響ユーザーに与える体験の質などです。

このインパクトの大小を根拠を以て判断し、対応コストを考慮した上でどこから手を付けるべきかを意思決定します。

言われた通りに物事を進めたら、何も進んでいなかった過去

偉そうにあれこれ語っていますが、これらはすべて過去の経験から学んだことで、自戒の意味もありこのテーマにしてみました。

まさに伝書鳩が原因で失敗した話ですが、とあるサービス課題を解消するために企画領域の担当者から施策の相談を受けたことがありました。

それを言われるがままタスクとして積み進行しようした際に、実装担当のエンジニアから指摘を受け、様々な観点において考慮すべき点が考慮されていないことが発覚しました。

発覚した時点ではすでに要件も仕様もfix、スケジュールもほぼ確定。

結局漏れていた観点を再考し、施策内容自体が変更になりスケジュールも引き直すはめに。

この失敗においては、あらかじめ施策の意図を理解し、プロダクトへの影響範囲や仕様や設計上の懸念点、リソース状況などを把握した上でどう進めるかを判断すべきでした。

f:id:trill_tech:20201216111449p:plain

とはいえ、例えば非技術者のPdMが実装についてすべてを把握したり、管轄外で走っている施策への影響を考えたりというのも現実的に難しかったりします。

ひとりで考え込まず、早い段階で有識者を巻き込み意思決定するというのもプロダクトマネジメントにおいては重要なポイントです。

担当プロダクトにおける「良し」を定義しておくこと

冒頭で「いまチームがどこを向くべきで、そのために何が必要かをメンバーに明示し、それをマネジメントすることができている状態」をPdMの果たすべき役割と述べました。

基本的に"改善"は、理想と現実のギャップをひたすら埋める作業だと考えています。

理想が存在しないプロダクトにギャップは存在しません。つまり、改善は行えません。

PdMは何よりもまず、プロダクトがどうなることが「良し」なのかを知っておくことが重要です。これがあらゆる意思決定における指針となります。

先に挙げた「"伝書鳩"になっている状態」は、プロダクトに対するPdMとしての意思が存在しないために他者の意見や依頼をそのまま受け入れざるを得ないことでそうなってしまっているというパターンが多いように思います。

月並みですが、プロダクトを理解し、ユーザーに寄り添い、どうなることがそのプロダクトにおいて理想かをひたすら思い描き、そこにチームを導く意思を持つことが脱"伝書鳩"の第一歩です。

f:id:trill_tech:20201216122205p:plain note.com

まとめ

今回は、"伝書鳩"が意思を持ってプロダクト開発を推進するために気をつけるべきことをお伝えしました。

  • プロダクトの理想形を思い描く(「良し」を知る)
  • 現状と理想とのギャップを知る
  • ギャップを埋めるために、施策の優先度を根拠を以て正しく整理する
  • 自分がチームを理想形に向かわせる意識を高く持つ

かくいう自分も初心忘るるべからず、これらの意識を念頭に置いて今後もTRILLを推進していきます。

明日は、TRILL開発部 フロントエンドエンジニアのmaseoさんによる「Google Optimizeでテストをしてる話」です。お楽しみに!

積極募集中

delyでは一緒にサービス成長させるエンジニアを積極採用中です。 興味のある方はぜひカジュアルにお話ししましょう!

join-us.dely.jp

また、delyではTechTalk という社内のメンバーがテーマ毎に話すイベントも開催していますので、こちらもぜひチェックしてみてください!

bethesun.connpass.com

Merged Manifest を使って uses-permission を調査した話

どもです、TRILLのAndroid担当してます永井です。

この記事は「dely #2 Advent Calendar 2020」の17日目の記事です。 adventar.org

「dely #1 Advent Calendar 2020」はこちら↓ adventar.org

昨日は @MeilCli さんの C# 9.0時代のnull判定解剖 という記事でした。
様々なnull判定の比較検証がまとまってますので、こちらもぜひ御覧ください!

さて

今回は APK で要求している uses-permission の手軽な解析方法について話したいと思います。

先日、新しく広告SDKを実装したAPKをビルドしていたところ、心当たりのない uses-permission が付与されていることに気づき、要求元を調査していました。

そこで直近実装したものを一つづつ外して追いかけようとしていたところ、
メンバーに Merged Manifest 使うと便利ですよーってアドバイスをもらい即解決できたのでこの感動と Tips を忘れないうちにまとめました。

やったこと

は超簡単で、まず AndroidStudio 内でプロジェクトの AndroidManifest.xml を開きます。
下タブに [Text] [Merged Manifest] とあり、[Text] には開いたマニフェストで宣言している定義値が並んでいますが、今回使うのは [Merged Manifest] の方です。

Android の APK に含めることのできる AndroidManifest は一つだけなので、外部ライブラリや Flavor などのマニフェストはビルド時に一つにマージされます。

[Merged Manifest] ではその一つにマージされたマニフェストの定義値を確認することができます。

f:id:trill_tech:20201124105842p:plain

f:id:trill_tech:20201124110019p:plain

少しみづらいですが色が使ってるライブラリと対応していて、uses-permission をクリックすると使用しているライブラリのマニフェストを表示することができます。
例えば選択行の READ_EXTERNAL_STORAGE および WRITE_EXTERNAL_STORAGE は leakcanary (デバッグ時のリーク検出ライブラリ)で定義されていることがわかります。

参考リンク developer.android.com

解析結果

今回謎だった uses-permission は

android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_PHONE_STATE

の3つで、今回広告を実装するにあたってデバッグ用に追加した AdMob のテストスイートの消し忘れによるもので、呼び出しコードを削除していたが build.gradle に依存が残っていて権限要求されていました。

また開発用デバッグ Flavor で有効になる leakcanary も

android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE

を要求していることがわかりました。

なのでテストスイートを外し改めてリリースビルドすることで無事不要な権限を要求することのないAPKをビルドすることができました。めでたしめでたし。

まとめ

出処不明な uses-permission やその他定義は AndroidManifest.xml の
[Merged Manifest] から簡単に追える。
使わないコードは依存も忘れず削除しよう。
以上です!

おわりに

明日はプロダクトマネージャーの Rice さんの 初心者PdMに贈る「"伝書鳩"が意思を持つために意識すべきこと」です。ぜってぇ見てくれよな!

delyでは一緒にサービス成長させるエンジニアを積極採用中です。 興味のある方は気軽にお話しましょう〜!
join-us.dely.jp

delyについて詳しく知りたいよって方は、TechTalk という社内のメンバーがテーマ毎に話すイベントもあるのでこちらも是非ご参加ください!
bethesun.connpass.com

Xcode OrganizerのScroll Hitch Rateについて

TRILL開発部の石田です。

2020年9月にXcode12がリリースされ、Scroll Hitch Rateという機能が追加されました。 今回はこの機能について紹介します。

Xcode Organizerとは

f:id:trill_tech:20201208130019j:plain

Xcode Organizerについて、Appleのドキュメントでは以下のように説明されています。

Appのクラッシュログ、エネルギーレポート、パフォーマンスに関する指標(お客様が使用した際のバッテリー消費量や起動時間など)を簡単に確認できます。

ユーザの端末からバッテリーライフやパフォーマンスデータ等の情報がAppleのサーバに送られ、それがXcodeのOrganizerに表示されます。 ただし情報を送信するのはプライバシー設定の「Appデベロッパと共有」をOnにしている端末に限られるようです。

ちなみに、Organizerでユーザの統計情報を確認するために追加の実装は不要です。

Scroll Hitch Rateとは

f:id:trill_tech:20201208130043p:plain

Scroll Hitch RateはXcode12からOrganizerに追加された機能で、アプリ内のスクロールのスムーズさを表現しています。

Scroll Hitchとはレンダリングされたフレームがスクロール中に画面に表示されないことで、これによってフレーム落ちし、スクロールが不安定な挙動となります。

iPhoneはフレッシュレートが60Hzなので、1フレームは16.67msであり、それ以上の時間がかかるとフレーム落ちし、ユーザ体験が下がります。

表示される指標

f:id:trill_tech:20201208130102p:plain

  • Hitch time: フレームが画面に表示されるのに必要な追加の時間の合計
  • Scroll duration: スクロール時間
  • Hitch rate = Hitch time / Scroll duration

Hitch rateが高ければ高いほどHitchが多く、ユーザにとって体験の悪いスクロールとなります。 逆にHitch rateが低いほどユーザ体験の良いスクロールとなります。

目指すべき数値

f:id:trill_tech:20201208130117p:plain

  • 5ms/s以下: 良いユーザ体験
  • 5ms〜10ms/s: ユーザがHitchに気づき始めるので調査すべき
  • 10ms〜: かなり使いづらいので早急に解決すべき

基本的に5ms/sを下回っていれば問題ないようです。 Xcode Organizerはアプリバージョン毎の結果が表示されるので、アプリをアップデートした際に改善しているか・悪化していないかチェックするのが良さそうです。

まとめ

Xcode12からScroll Hitch Rateという機能が追加され、スクロールのスムーズさ(ユーザの手元で起こっているもの)が定量的に判断できるようになりました。 TRILLでも定期的な確認と改善をしていき、ユーザ体験をより良いものにしたいと思います。

delyでは全方面でエンジニアを積極採用中です。 興味のある方は是非お声がけください。

join-us.dely.jp

参考

https://developer.apple.com/videos/play/wwdc2020/10076/

https://developer.apple.com/videos/play/wwdc2020/10077/