コネヒト開発者ブログ

コネヒト開発者ブログ

レコメンドエンジン導入までの取り組みとアーキテクチャについて

こんにちは!MLエンジニアのたかぱい(@takapy)です。

今回は、ママリのアプリ内にレコメンドエンジンを導入したので、導入までの取り組みやアーキテクチャについてご紹介できればと思います。


目次


ママリ内での課題

ママリはサービスとして6年目を迎え、サービスの成長とともにアプリ内の記事数も増えており、それに伴いユーザーが本来欲しい情報にたどり着くことも難しくなってきました。

加えて「子育て層のユーザー」という切り口1つとっても、0才児のママと1才児のママでは悩みや欲しい情報がまったく異なります。

このような背景から、これまで人的に行っていたルールベースでの記事配信に対し課題を感じており、機械学習で解決できないか、ということで取り組み始めました。

アーキテクチャ概要

全体のアーキテクチャは以下のようになっています。

アプリのログはBigQueryに蓄積されているため、そこからデータを取得・構造化した後、推薦記事を計算してDynamoDBに保存しています。

APIはFlaskで構築し、APIのログはAthena経由でRedashから参照できるようにしています。
このRedashはBigQueryのデータも参照できるので、ここでAPIのログとBigQueryの行動ログデータを突き合わせて、各種指標(CTRなど)をモニタリングしています。

f:id:taxa_program:20200330195638p:plain
アーキテクチャ

EDAとアルゴリズムについて

まずはデータの理解を深めるために、簡単なEDAを実施しました。
ママリの記事にはカテゴリ*1がついているのですが、あるユーザー属性別にカテゴリ毎の閲覧数を見てみると、大きな偏りがあることが分かりました。
(例えば、ユーザー属性Aの人は、「子育て・家族カテゴリ」の記事をよく見ているが、ユーザー属性Bの人は「住まいカテゴリ」の記事をよく見ている、など)

f:id:taxa_program:20200330184036p:plain
特定カテゴリにおけるユーザー属性別の記事閲覧数例

この結果を元に、ユーザーを数十種類のクラスタにハードクラスタリングし、「クラスタ単位」と「クラスタ×ユーザー単位」の2種類に分け、推薦記事を計算しました。

「クラスタ単位」とは、以下のようにクラスタ粒度でRatingテーブルを作成して推薦記事を計算するイメージです。こうすることで、新規ユーザーに対してもある程度嗜好性を考慮した記事を推薦してくれることを期待しています。

f:id:taxa_program:20200330184949p:plain
クラスタ単位の推薦記事計算例

「クラスタ×ユーザー単位」とは、ユーザーごとにそれぞれのクラスタ内で推薦記事を計算するイメージです。

f:id:taxa_program:20200330185134p:plain
クラスタ×ユーザー単位の推薦記事計算例

また、今回は「0→1」での導入だったため、レコメンドの精度よりは実装までのスピードを意識して取り組みました。
そのため、推薦アルゴリズムには業界内での成功事例があり、かつ比較的実装コストも低い協調フィルタリングと、Matrix Factorization(MF)を採用しようと決め、この2つのアルゴリズムでオフライン検証に進みました。

オフライン検証の失敗と学び

全ユーザーの直近3週間の行動ログ(アプリの閲覧履歴、ユーザーのアクションを含む)をBigQueryから取得し、構造化しました。
そこからデータを時系列に、古い2週間と直近1週間に分割し、前者を学習データ、後者をテストデータとしてオフライン検証を行いました。

オフライン検証では、推薦したアイテムをクリックするか否かを評価するため、Recallという指標を用いました。Recallとはユーザーが実際に嗜好したアイテムのうち、推薦したアイテムでカバーできたものの割合です。

結果、このオフライン検証では良い数値がでませんでした。

そもそもオフラインでの評価は、我々がおすすめしようとするまいと、ユーザーが嗜好(クリック)したものを予測しているので、それを正確に予測できることにどれだけ意味があるのか、という議論ポイントはあると思っています。
そんな中でオフライン検証ではあまり良い予測ができないケースもある(良い予測ができても、それがそのままオンラインでの精度にならない)ということを学べたのは、今回の収穫の1つであったと思います。

とはいえ、PoCばかり進めていても仕方ないよね、ということから、未知の部分は多いもののプロダクションでどういう動きをするか見てみよう、ということでA/Bテストを実施しました。

A/Bテストについて

冒頭で説明したアーキテクチャは最終的なもので、A/Bテスト時の構成はかなりシンプルにしました。

今回、オフライン検証で良い数値が出た訳でもないため、現状の表示条件(ルールベース)よりCTRが下がる恐れがありました。
そんな中で全体のアーキテクチャを先に設計・構築してしまうと、実装までのスピードが遅くなったり、仮に指標が下がってしまった場合の時間/金銭的コストが大きくなってしまいます。

そのため、下図のような最小限の構成としました。

f:id:taxa_program:20200330191934p:plain
A/Bテスト時のアーキテクチャ

結果的に、A/BテストではCTRが7ポイント向上し、検定を行いそのCTRに有意差が確認できたことから、プロダクションへ本格導入することに決定しました。

レコメンドアルゴリズムについて

今回試した2つのアルゴリズムについて簡単にご紹介します。
(詳細な実装方法などはWeb上に優良な記事がありますので、ここでは概要の説明に留めます)

強調フィルタリング(アイテムベース)

協調フィルタリングは大別して「ユーザーベース」と「アイテムベース」の手法があります。今回実装したのはアイテムベースの協調フィルタリングです。

まず、上記Rating行列から全てのアイテム間の類似度を計算して、類似度行列を生成します。この時の類似度計算方法には「ユークリッド距離」や「コサイン類似度」を使用できます。今回はコサイン類似度を使用しました。

f:id:taxa_program:20200330192217p:plain
類似度行列作成例

次に、推薦アイテムを計算したいユーザーのベクトルと、この類似度行列の積をとります。
下記例では、user Dに対して推薦したいアイテムを計算しています。計算結果からすでに評価しているitem 2を除外すると、item 4の値が一番高いため、user Dにはitem 4を推薦すればよい、ということになります。

f:id:taxa_program:20200330192410p:plain
userDに対する推薦記事計算例

Matrix Factorization

Matrix Factorizationはその名前の通り、Rating行列をuserの特徴量行列(P)とitemの特徴量行列(Q)に分解します。
例えば、m人のユーザーとn個のアイテムを考えたときに、m > k > 0であるk次元に次元削減して変換することを目的とします。
これは、評価値を表すRating行列(R)を、ユーザー要素を表すk × mの行列(P)と、アイテム要素を表すk × nの行列(Q)に近似することです。


R \approx PQ^T

図にすると以下のようなイメージです。

f:id:taxa_program:20200330193600p:plain
行列分解例

そして、分解された2つの行列の積をとると、新しいuser×itemのRating行列が生成されます。このRating行列は密な行列となるので、値の高いアイテムをそのまま推薦すればよいことになります。
例えば、user Aに対してはすでに評価しているitem 1とitem 2を除外すると、item 3を推薦すればよい、ということになります。

f:id:taxa_program:20200330193324p:plain
最終的な行列

最後に

今回は0からレコメンドエンジンを構築するまでの取り組みや、そのアーキテクチャについてお話しました。

私自身、レコメンドエンジンの構築はMovieLensのデータセットなどを使ってやってみた経験しかなかったので、実サービスに対して取り組むことで、実際にユーザーの反応を見ることができたり、安定して配信するための基盤作りに携われたり、とても面白かったです。

今回のレコメンドエンジンはまだまだ課題もあり、これからユーザーの反応を見ながらアップデートしていく予定なので、その時はまた取り組みをご紹介できればと思います。

*1:「子育て・家族」「病院」「住まい」など

Sign in with AppleでのiOSアプリとサーバーとの連携

こんにちは!エンジニアの柳村です。

Twitterなどの3rd partyのログイン機能を提供しているアプリは6/30までに対応が必要です。(2ヶ月延期されましたね!)

アプリ単体でSign in with Appleをできるようにするのはとても簡単です。しかし大抵のアプリの場合はそれだけでは完結せず、サーバー側でSign in したユーザーと紐付ける必要があります。

サーバー側はFirebase AuthenticationやAuth0といったIDaaSにまかせるという手もありますが、今回は自前で実装することを前提にその実現方法を見ていきたいと思います。

全体の流れ

クライアント側とサーバー側のざっとした流れはこのようになります。

f:id:yanamura:20200327094652p:plain
sign in with apple flow

クライアントからサーバー側にid_tokenを渡すやり方とauthorization_codeを渡すやり方の2つの方法がありますが、ここではauthorization_codeを使ったやり方のみ紹介します。また、nonceの生成やクライアントサーバ間の受け渡し方法についても割愛します。

iOSアプリ側

Apple Developer Programでやること

アプリのIdentifierのCapabilitiesにSign in with Appleを設定します。 その際に、configureボタンを押してPrimary App IDを設定します。

また、Identifierを更新するとProvisioning ProfileがInvalidになってしまうので更新する必要があります。

XcodeのProject設定でやること

TARGETSのSigning&Capabilitiesで+Capabilityを押してSign in with Appleを追加します。

実装

iOSアプリ側でやることは、Sign in with Appleをするボタンを用意し、ボタンが押されたらASAuthorizationAppleIDProviderを生成し、performRequest()を呼ぶとログインに成功するとcallbackが呼ばれ、authorization_codeが取得できるので、これをサーバーに渡すだけです。(ドキュメント: Implementing User Authentication with Sign in with Apple)

ボタンの用意

import AuthenticationServices
...

let appleButton = ASAuthorizationAppleIDButton() // デザインを変えたい場合はUIButtonなどに変えれば良い
appleButton.rx.controlEvent(.touchUpInside)
    .subscribe(onNext: { [unowned self] in
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.email]
        request.nonce = `something` // nonceについては割愛します

        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    })
    .disposed(by: disposeBag)

delegateの実装

extension XXViewController: ASAuthorizationControllerDelegate {
    @available(iOS 13.0, *)
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        switch authorization.credential {
        case let appleIDCredential as ASAuthorizationAppleIDCredential:
            // ここでauthorizationCodeやidTokenが取得できる。
            print("#apple \(String(data: appleIDCredential.identityToken!, encoding: .utf8))")
            print("#apple \(String(data: appleIDCredential.authorizationCode!, encoding: .utf8))")

            // サーバーにauthorizationCode送る処理
        default:
            break
        }
    }
}

extension XXViewController: ASAuthorizationControllerPresentationContextProviding {
    @available(iOS 13.0, *)
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return view.window!
    }
}

サーバー側

Apple Developer Programでやること

Keysでkeyを追加します。 Sign in with AppleをENABLEにし、configureボタンを押してPrimary App IDを設定します。 作成が完了すると秘密鍵(.p8)をダウンロードします。この秘密鍵と作成したKeyのKey IDは、次でclient secretを作成するのに使います。

サーバー側の実装概要

サーバー側でやることは大きく2ステップで、まずAppleの/auth/token APIを叩いてid token(JWT)を取得します。 次にid tokenをAppleの/auth/keys APIを叩いて取得した公開鍵でverifyしてuser identifierを取得します。

id tokenの取得

1. client secret(JWT)の作成

以下のheaderとpayloadを使ってJWTを生成します。

header

{
  alg: ES256,
  kid: // Apple Developer ProgramのKeysで作成したKeyのKeyID
}

payload

{
  iss: // TEAM ID,
  iat: 今の時間,
  exp: 今の時間+適当な値(max 15777000),
  aud: https://appleid.apple.com,
  sub: // Bundle ID
}

署名はApple Developer ProgramのKeysで作成したときにダウンロードした秘密鍵を使います。

2. /auth/tokenにPOSTする

以下のデータをhttps://appleid.apple.com/auth/tokenにPOSTします。API仕様

{
    client_id: // Bundle ID
    client_secret: // 1. でつくったclient secretをいれる,
    code: , // iOSアプリから受け取ったauthorization_codeをセットする
    grant_type: 'authorization_code',
    redirect_uri: ""// 空文字
}

成功するとid_tokenが含まれたJSONが取得できます。

id tokenのverify

上で取得したid token(JWT)をverifyする必要があります。

1. 公開鍵の取得

https://appleid.apple.com/auth/keysをGETするとJWKSが取得できます。(API仕様

これには複数のJWKが含まれています。この中から、JWKのkidとid tokenのheaderのkidと一致するものを使って公開鍵を生成します。

2. id tokenのverify

ドキュメントVerify the Identity Token に以下のように記載があります。

- Verify the JWS E256 signature using the server’s public key

- Verify the nonce for the authentication

- Verify that the iss field contains https://appleid.apple.com

- Verify that the aud field is the developer’s client_id

- Verify that the time is earlier than the exp value of the token


これに従って、1. で取得した公開鍵を使ってid tokenをverifyし、id tokenのpayloadのnonce, iss, aud, timeを検証します。

id tokenのpayloadに含まれる内容は以下になります。(ドキュメント)

{
  iss: 'https://appleid.apple.com'
  sub: // The unique identifier for the user.
  aud: // Bundle ID
  exp: // expire time
  iat: // The time the token was issued.
  nonce: // nonce
  nonce_supported: true or false, 
  email: // The user's email address.
  email_verified: true or false,
}

subがuser identifierになるので、これを使ってユーザーを識別します。

まとめ

このように、iOSアプリ側は割と簡単に実装できますが、サーバー側は結構やることがあります。Googleみたいにサーバ側用のライブラリが用意されていればよいのですが、残念ながらAppleは用意してくれていません。。Githubにいくつかライブラリがありましたが、実装が間違っている(特にid tokenのverify周り)ものがときどき見受けられたので選定には注意が必要かなと思いました。Sign in with Appleを導入し、かつ自前でサーバー側も実装する場合は余裕を持って取り組んだほうがよさそうかなと思いましたので、まだ3ヶ月くらいありますが早めに対応したほうがよさそうでした。

最後に宣伝です!

コネヒトではエンジニアを募集しておりますので少しでも興味のある人はお話だけでも聞きにきてください! www.wantedly.com

Zoomウェビナーを使ったオンライン勉強会の裏側

こんにちは。2017年11月にAndroidエンジニアとしてjoinした関根です。03/11(水)にリモートワークを考えよう、というテーマでオンラインの勉強会を開催しました。パネリストとオーディエンスのみなさま、誠にありがとうございました!今回はその勉強会の配信の裏側を書かせて頂きます。

なお、当日の様子とLTスライドは下記の記事内に掲載しています。 www.wantedly.com

開催する上で考えたこと

今回のイベントは普段のオフラインの勉強会とは違い下記のような制約がありました。

  • パネリストがオンラインかつ複数拠点で発表できること
  • オーディエンスがなるべく手間をかけずに参加できること
  • オンラインでもLTに対して質疑応答が可能なこと
  • オンラインでも参加者全員がコラボレーションの体感が得られること

これらの制約をクリアし、どのようなツールでどのようなコミュニケーション設計にすれば、普段の勉強会と変わらない体験を提供できるかのかを考えるところから始めました。

どのツールを利用するか

結論から書くと今回はタイトルにある通りZoomウェビナーを利用しました。 選択した理由としては下記の点です。

  • 画面共有機能でスライドを共有できるのでパネリストが集まる必要がなかったこと
  • 質疑応答機能、チャット機能、手を上げる機能など双方向のコミュニケーションを取る機能が充実していたこと
  • 視聴者はアプリのインストールが不要で参加できること

コネヒトでZoomを試験導入中で使い慣れていたことや、ビデオ通話が高品質で安定していたことが理由としてありますが、先述した制約の全てを解消することができたのでZoomを選んで正解だったと考えています。注意点ですが、Zoomウェビナーはプランにより最大参加人数が決まっているので、最適なプランを選びましょう。*1

配信トラブルがあった場合

オンラインイベントがはじめてだったので、イベント時に配信トラブルが起きることも考えられました。Zoomウェビナーにはレコーディング機能があり、後日動画を公開することが可能で安心感がありました。 録画したものはYoutubeに公開していますので、是非ご覧ください! www.youtube.com

コミュニケーション設計はどうするか

オフライン勉強会を開催が決定して一番悩んだのが、コミュニケーション設計をどうするかでした。 普段の勉強会とは違い全参加者がオンラインでコミュニケーションをとる必要があるので、普段の勉強会とは違った難しさがありました。

参加者の役割と可能な操作

まず簡単にZoomウェビナーの各参加者の役割を解説します。 この役割のメンバーがそれぞれにどのようにコミュニケーションをとってもらいリモートワークへの理解を深めるかを考えなければいけませんでした。

役割 ビデオ通話 チャット Q&A投稿 手を挙げる機能
ホスト オペレーター できる できる 回答のみ できない
パネリスト 登壇者 できる できる 回答のみ できない
オーディエンス 観客 パネリストに昇進することで可能 できる 質問と質問への投票 できる

これを前提のどのようなコミュニケーションを取ることが最適か相談し方針を決めていきました。概ね以下のような議論を行いました。

コミュニケーションの課題と対策

オフラインでのリアクションをオンラインでどのように再現するか?

オフラインの勉強会では自然と生まれる拍手や歓声がオンラインでは難しいので以下のような状況が想定されました。

  • パネリストにオーディエンスの反応が伝わりにくい
  • 全参加者が勉強会に参加してる感が薄まる

これはチャット機能を利用し、👏👏👏👏👏👏👏や888888888のようなコメントを書き込んでもらうことで登壇者に参加者のリアクションが伝わるような工夫をしました

質疑応答はどのように実施するか?

オフラインの勉強会で行われているのと同じように、質疑応答の時間を取らないとリモートワークの知識を深める場としての意義が少し低下してしまう様に思いました。これについてはZoomウェビナーの機能を踏まえ以下の2つの案があがりました。

  1. 質疑応答機能を利用して、テキストで質問する。
  2. 手を挙げる機能を押したオーディエンスのビデオ通話を許可し直接質問してもらう

これはZoomが初めての参加者が多くいることが予測されたので、オーディエンスがよりシンプルな操作で質問できる2.の案を採用しました。

ホストとパネリストのコミュニケーションはどうするか?

オフラインのイベントでは、イベント中のコミュニケーションは、カンペを出す、耳打ちをするなどで取ることが可能ですが、今回はパネリストの方がそれぞれ別の場所にいるため、他の方法を取る必要がありました。これは以下のような案があがりました。

  1. Slackなどチャットツールで連絡を取り合う
  2. Zoom内でホストとパネリストのみへの発言機能を利用する

結論としてZoomウェビナーのチャット機能では誤操作の可能性があったため、Slackでコミュニケーションを取る方法を採用しました。

これ以外にも相談したことは数多くあるのですが、紹介しきれないのでここまでとし、次は当日の流れを紹介します。

当日の流れ

当日は以下のタイムラインで進行をして行きました。

17:30 - 18:30(リハーサル)
18:50 - 19:00(前説)
19:00 - 19:40(LT&質疑応答)
19:40 - 19:45(結び)

それぞれの様子を簡単に紹介して行きます。

リハーサル

Zoomウェビナー機能の実践セッションを利用しホストとパネリストで、リハーサルを行いました。 パネリストの皆様とイベント開始前に簡単な打ち合わせをしながら下記の確認をしました。

  • 画面共有の操作手順の確認
  • 映像と音声の見え方聞こえ方チェック
  • 質疑応答の流れの共有

他にも質疑応答時の流れをリハーサルしたり、オーディエンスからの見え方を確認するなど、ギリギリまでバタバタとしながら本番に臨みました。

前説

司会からオーディエンスに向けて以下のような説明を行いました。

  • 登壇者の画面がみれているかの確認
  • リアクションの取り方の説明
    • チャットの画面を開いて、リアクションの練習をしてもらう
  • 質疑応答の説明
    • 手をあげる機能を利用してもらう
    • ホスト側でビデオ通話を許可するので直接質問してもらう

開始時に会場側でハウリングが発生したり*2、司会の映像が遅れるなどのトラブルもありましたが、参加者のみなさんに、前説からチャットで盛り上げていただいたので、あたたかい気持ちでスムーズにイベントを開始することができました。

LT&質疑応答

概ねスムーズに進行できたと思いますが、はじめてのオンラインイベントらしく、下記のようなちょっとしたトラブルもありました。

  1. 手を挙げる機能を利用した質疑応答で、立候補者がでなかった
  2. オーディエンスのチャット設定がパネリストだけに見えるようになっていた

これらのトラブルにも以下のように、参加者の皆さまのご協力により、それぞれ対応をすることができました。

  1. パネリストの方にチャットから質問を選んでもらうやり方に切り替える
  2. 司会からチャットの設定を変更するようにオーディエンスに促してもらう

パネリストとオーディエンスの皆さま、スムーズなイベント進行にご協力いただきありがとうございました!!

反省点

今回はじめてオンライン勉強会を開催したため、様々な点で反省はあるのですが、特に質疑応答の体験をもう少しよくできたと感じています。次回開催時はZoomの質疑応答機能を利用してみたいと思っています。 最後となりますが、今回のイベントの経験オンライン勉強会用のチェック項目を掲載し、本記事の締めとさせて頂きます。*3

オンライン勉強会用チェック項目
事前準備

- 周りの音が入り込まない静かな環境を用意できるか
  - ヘッドフォンがあると安心
- 配信で利用するPCは安定して映像音声を届けられるか
  - 可能ならば予備のPCを用意しておく
- オンラインでのリアクションを決める
- 質疑応答の方法/流れを決める
 - オーディエンスへの説明の方法も一緒に決める
- 録画機能を利用するかを決める
  - 事後配信をするかも一緒に
- 参加者に配信URLを共有するタイミングを決める
- リハーサル時間を当日に用意する
   - 特に質疑応答の手順は複雑なので入念に

本編中

 - ハウリングに気をつけよう
 - オーディエンスのコメント設定は全員向けになっているか
   - デフォルトではパネリストにだけ見える設定
 - 周りの雑音に気をつけよう
 - 録画機能状況に気をつけよう
 - 配信画面への映り込みに気をつけよう

ここまでお読み頂きありがとうございました!

*1:ZoomミーディングのアドオンなのでZoomミーディングのプロプラン以上の契約も必要です。

*2:犯人は私です

*3:Zoomウェビナーの文脈が強めです

リモートワークについて考える勉強会(オンライン開催)でもらった質問にすべて答えます

こんにちは、エンジニアの@dachi_023です。03/11(水)にリモートワークを考えよう、というテーマでオンラインの勉強会を開催しました。発表・参加していただいた皆さん、ありがとうございました!発表資料や雰囲気などは下記の弊社広報ブログにまとまっていますのでこちらもぜひご覧ください。

www.wantedly.com

近い内に録画した内容に字幕等つけたものをアーカイブ配信する予定ですので途中からしか見れなかった方や気になった方はそちらをお待ちください(というのを気軽にできるのもオンライン配信のいいところだな、と思いました)。

チャットでもらった質問について

今回はZoomのウェビナー機能を使って発表&その場でQAをしたんですが、時間の関係ですべての質問にはお答えすることができなかったので本投稿ですべてお答えしていきます。

※ 発表時に回答した質問についても再掲(もしくはより詳細に回答)しています。

職種によってリモート率とか変わったりしますか?

「この部は全然リモートしてないね」とかはないです。webサービスの会社なので基本はリモートワークでも働けます。ただ、会社に用事がある場合等は各々の判断で通勤のピークタイムを避けつつ出社しています。

sneekで背景見られたくない方とかはどうしてるんでしょうか?

(Zoomにはバーチャル背景があるが、他のサービスにはない。どうするか?という話)

Snap Cameraのフィルターには、顔の加工だけではなく背景も加工してくれるものがあるのでそういうのを使うといいかもしれません。

f:id:dachi023:20200312212651p:plain
例えばこれはナゲットなんですが、背景付きなので部屋が映りません

リモートワークで不便はありますか

導入直後はツールの使い方に慣れていないため、通話中に一斉に喋ってしまったりとかしてワチャワチャしてしまうことが多かったです。あとは子供が仕事中に絡んできたりするので最初は大変でした。

リモートワーク推進メンバーは、どうやって選定されましたか?

立候補した人で構成されています。そのメンバーで使えそうなツール候補のリストを作り、検証しながらどれを正式採用しましょうかね?という話を進めていきました。

リモートワークでも勤務時間ってあるんですか?

コアタイムがあるのでその時間内は確実に働いているようにしています。それ以外は家庭の状況などに合わせて働いています。なので、勤務時間の管理はオフィスワークしていた頃と変わらずやっています(打刻はSlack上からやっています)。

リモートで使用するツールの導入判断で、意外と重要なポイントってなにかありましたか?

リモートワークに限らないのですが「安定しているか」は大事だと思いました。例えば、Slackが頻繁に落ちるサービスだったらコミュニケーションが全然取れなくなってしまうリスクがあるので選びません、といった感じです。あとはツールを増やしすぎないように気をつけました。もちろん必要なら入れるんですが、複数のツールを組み合わせないといけないとなると単純に面倒なので・・・。

リモートワークを導入するのに大変だったことはありますか?

どういう心持ちでリモートワークすればいいだろうね、とかどういうツール使うと仕事が捗りそうかな、とかそういうドキュメントを沢山作ったのでその辺は結構時間がかかったかもしれません。あとは、私が対応したわけではないのですが、セキュリティまわりの対応とかは大変そうだなあと思って見ていました。

各自、在宅での誘惑はどのように向き合うようにしていますか?

私の場合はテレビが絶対見ることのできない方向にデスクを置いてます。今は目の前に壁があって、壁を見るかディスプレイを見るか、くらいしか選択肢がありません。あとは環境音とかがなるべく聞こえないように&通話したい時にすぐ繋げられるようにワイヤレスイヤホンを付けっぱなしにしています。

Zoom慣れない方へのレクチャーで工夫したことはありますか?

ミーティングの始め方はどうやるか?というレクチャーは厚めにやりました。Zoomはミーティングの設置だけでも設定する項目が多く面倒なので、ZoomのSlack App使ってミーティング開始するとログイン以外は不要で簡単ですよ!とかGoogle Calendarの予定にZoomのURLを紐付けると探しやすいよ!とかそういう話をしました。簡単に始められそうだな!という感覚を持ってもらうのを大事にしました。

初めてリモートワークをする人や、社内の人の反応はどうですか?

そこまで不便はしてなさそうかな?と思っています。不便だなと思った点などは共有してもらってなるべく早めに改善するようにして、ずっと不便な状態にはならないように気をつけています。

Zoom のホスト権限の管理などはうまく回ってらっしゃるのでしょうか

現時点だと推進メンバー、マネージャー以上のメンバー、人事や営業など外部の方とミーティングを設定する必要がある人に付与しています。マネージャー以上のメンバーとしているのはマネージャーに付与することで各チームに1人以上はホストがいる状況が作れるからです。

リモートのタスク管理とかで、問題とかありますか?

Asana, Trello, GitHub Project, ZenHubなど、チームごとに使っているものは異なりますがオンライン上で管理しているので特に困ることはありませんでした。あとはデイリースクラムや朝会などをZoomで開催し、タスクの確認をしてチーム内の認識を揃えるようにしています。

リモートで仕事外のコミュニケーションはどう取っていますか??(リモートランチや飲み会とか)

飲み会はやったことないですがランチはやりました(ちょうどこの発表当日の昼休みでZoomで繋いでランチしました)。最初は億劫になりがちなんですが慣れてくると良いものです。

管理が行き届かない場合のトラブル例とかはありますか?

コネヒトではトラブルとかは発生していないんですが、例えばメンバーの管理という観点で見ると「サボらないんですか?」はよく聞かれます。個人の意見としては、そこを管理する必要はない(= サボった分あとで頑張らないといけないのはサボった人なので・・・)と思います。モノの管理の話だと、リモートワークのために自宅に会社の備品などを持ち帰る必要があったりするのでその辺の管理は各自お願いしますね、とかでしょうか。

さいごに

以上となります!予想以上に多くの質問をいただくことができました。また、皆さんのリモートワークへの関心の高さも伺えたような気がしています。今後も引き続き参考になりそうな情報を発信していきます💪

PR

先月書いたブログでもまとめていますのでこちらも良ければ読んでみてください。

tech.connehito.com

社内向けにまとめていたリモートワークの知見をすべて公開します

こんにちは、エンジニアの@dachi_023です。先週くらいから新型コロナウイルスの感染拡大に伴って在宅リモートが導入された、という内容のプレスやニュースをよく見かけるようになりました。コネヒトでも同様に在宅リモートの推奨、ラッシュ時の電車通勤は原則禁止、などのアナウンスがあり、これまでリモートワークをしていなかったメンバーもリモートワークを導入して自宅から働いています。

ですが、いきなり「皆さんそれではリモートワークしてください!はいどうぞ!」と言われても家からどのように働けば良いのか・・・となり、ただただ生産性が落ちてしまうだけです。もちろん健康第一で導入されている今回のリモートワークではありますが、出来ることなら効率をなるべく落とすことなく働きたいです。

コネヒト開発部では今年の頭からリモートワークの試験導入を行っており、エンジニアとデザイナーが週に2日ほどリモートワークしてどういったツールを使うと良いかとか、ビデオ会議やチャットでのコミュニケーションを改善して業務が円滑に進むようにするためにはどうしたら良いかなどの調査や検証をしていました。本記事ではその検証結果を公開し、今回の件で初めてリモートワークを導入した企業の方々の参考になれればと思っています。

リモートワーク導入にあたり理解しておきたいこと

在宅で働くことはオフィスに来て働くことの上位互換ではないです。通勤がなくなるのでそこに関しては最高なんですが、チャットはやり取りに少し時間がかかったり、ビデオ通話は多少とはいえ遅延があり最初のうちは会話しづらく、オフラインで対面で話すのに比べてコストがかかることもあります。などなど、それぞれで得意なこと、不得意なことがあります。そうした状況をリモートワークするメンバー、オフィスにいるメンバーがそれぞれ理解して、協力しながら進めていく必要があります。

リモートワークはノリだけで始めると辛い

色んな記事で散々書かれていることですが、リモートワークは「スキル」です。どうやったら物理的に離れているメンバー同士でうまいこと協力していけるか、を考えて実践し、改善していかなければいけません。個人で改善できることもありますし、会社としてリモートワークが快適に行えるような土台を作ることも必要になってきます。会社にいる時と同じやり方では上手くいかないことも多々発生するのでそういった点を解決していくのが導入後すぐに発生する仕事になると思います。

チェックリストを作成する

全社向けに自宅の環境やリモート時の意識についてのチェックリストを作成しました。これによって何があった方がいいのか、在宅で仕事をするためにやっておくべきことをある程度把握してもらいたい、という狙いがあります。

自宅環境の整備について

必須なもの

  • ✅ インターネット回線は契約してあるか
    • モバイルルーターなどでも可 (ただし光回線の方が速い・安定する)
    • 自身の業務を行うのにストレスのない回線速度が出ているか
  • ✅ 作業スペースは確保できているか
    • 近くにPC等を充電できるようコンセントがある
  • ✅ ビデオ会議用のイヤホンはあるか
    • 自分が使いやすいものであれば何でもOK
    • 移動する際とか面倒なのでワイヤレスがオススメ
  • ✅ カメラはあるか
    • カメラがついているノートPCであればそれを利用する
    • ついていないモデルの場合は購入することをおすすめします
  • ✅ 誘惑を断ち切れるか
    • テレビなどのコンテンツに意識を持っていかれていないか
  • ✅ 残業時間を気にする意識はあるか
    • いつでも働ける状態なので気をつける
    • 適切な業務量と時間を維持する
  • ✅ 家族、パートナーに在宅で働くことが理解されているか
    • 業務時間中に家事をお願いされる
    • 話しかけられる頻度が多い
    • 働かないと・・・でも家の事も・・・みたいになって辛い

あればなお良い

  • ✅ Wi-Fiが使えるか
  • ✅ 長時間座っていても身体が痛くなりにくいデスク、椅子などはあるか
  • ✅ 外付けディスプレイはあるか

オフィスにいるメンバー向け

リモートワークしているメンバーと一緒に働くためには以下に注意しましょう。

  • ✅ オフラインでしか得られない情報を無くす
    • 会社来い!って言ってるのと変わらなくなっちゃうので
    • 必ずオンラインのどこかで確認できるようにすること (Google Drive, 情報共有サービス, など)
  • ✅ 勤怠などについて監視しすぎない
    • 所詮カメラで映している部分しか動きは見れないし、互いに疲弊するのでやめる
    • プロセスよりも最終的なアウトプットを評価する
    • ただし、今働いているかどうかは知れたほうがいいので始業・休憩・終業くらいはルール化する

使用しているツール

リモートワーク時に使用しているツールは下記の通りです。

2つ書いてあるところはまだ検証している途中のもので、部署やチームによって使っているツールがバラバラな状態です。最終的にはどちらかに統一される予定になっています。

以下、必須ではないものの使ってみたら意外と良かったものを紹介します。

ゆるい繋がりを生み出すツール

SneekとPukkaTeamは一定間隔で写真を撮ってチームメンバーの現在の状況を共有するためのツールです。どちらもモザイク機能があり顔や部屋を映したくない場合にも利用できます。効果は人による、といった感じで特にメリットを感じない人もいますが、見られている感があって仕事をやる気になる、という人もいました。どちらのツールも機能はほとんど一緒なのですが、Sneekはウィンドウサイズに合わせて写真のサイズが変わるので小さくして画面端に置いておくのに便利でした。

ノイズキャンセリング

Krispをインストール後にZoomやHangoutsの入力側として設定すると環境音などを軽減してくれます。今のところ軽減が確認できたのは以下です。

  • テレビの音
  • 外を走っている車の音
  • 強風時の風の音

テレビに関しては結構な音量で流れていたにも関わらず実際に通話した人に聞いてみると「たまにうっすらと聞こえる程度で差し障りはない」とのことだったのでそれなりに効果がありそうでした。

f:id:dachi023:20200221210936p:plain
Zoomの例。krisp microphoneを選択することで有効になる

スピーカー側の設定もあるのですが、普段使っているイヤホンでノイズキャンセリングを有効にしたら音声がブツブツと切れるようになってしまったため私は無効にしています。

チャット上のコミュニケーション

普段から利用しているSlackでのコミュニケーションにおいても、今一度意識してみてねということで社内に展開したものが以下になります。なのでリモートワークだからやってくださいね、という内容ではないです。

利用頻度を落としすぎない

  • ひとりでいるとチャット画面と向き合う時間が少なくなりがち
    • 周りに人がおらず、ずっと集中しているので大事な用がある時くらいしか開かなくなりがち
    • リモート解禁でただでさえコミュニケーションコストが上がるので更に上がらないようにする
    • いつでも話しかけられる、話しかけられやすい状態を作れるよう意識する
  • また、通話やビデオ会議の際によそよそしくならないようにしておく
    • この人と久々に話したかも、となると気まずい感じになったりするのでチャットコミュニケーションも大事にする

とりあえず反応する

  • メンションなど来た時にとりあえず反応だけしておくと相手が見てくれていることを確認できる
    • 「後でちゃんと返します!」でもいいのでやると良い
    • 都度ちゃんと返す、をやり始めるとずっとチャットに張り付くことになる
      • 非同期コミュニケーションの良さがなくなるためそれは避けたい
  • 共有だけの連絡などに関してもEmojiでリアクションするなりして見たよ、が分かるようにする

雑談も大事にする

  • 対面で話す機会が減るのでそこを補うようなイメージ
  • コネヒトのSlack上には趣味などに関するチャンネルがあるのでそういったものも活用していく

不安を抱えておかない

  • 誰も気づかないまま自分だけがモヤモヤしていってしまうため
  • 書けばだいたい誰かが拾ってくれるので積極的に書く

察してくれは無理

  • 物理的に離れているため表情や雰囲気はほとんど分からない
    • ビデオ通話してたとしても見えている範囲は限られるのでオフラインに比べるとやはり弱い
  • 書いたことしか伝わらない、くらいの感覚でいたほうが良い
  • テキストのやり取りは含められる情報(感情とか)が少ないのでなるべく伝わりやすい文章にする

やりとりの数を減らす

  • 1回で伝えられる量を意識しないと何度も質問をすることになり時間がかかる
  • Yes / Noで答えられるような投げかけを意識するなど、工夫してコミュニケーションを取るようにする
  • また、話が長引きそうだなと思った時に速やかに通話に切り替えるなどができればそれでも良い

チャットだけに頼らない

  • チャットでのコミュニケーションは気軽だが前述したとおり情報が他の手段に比べ少なくなりやすい
  • より深く話し合いたい内容がある時などは通話等の手段を使えるようにしておく
    • = 通話に対しての苦手意識を減らしておく
    • 通話もオフィスで対面で話すのも同じだとイメージすると良い

ビデオ会議中のコミュニケーション

リモートワーク解禁によって初めて利用する人も多いであろうビデオ会議に関しても意識しておきたいことをまとめて展開しました。

1人1台PCを用意して通話を繋ぐ

  • オフィスにいる人達が1台だけ接続して通話するのは良くない
  • 3人以上だともう無理。2人なら全員映るからまだ許せる
  • 1台に複数人張り付いている場合
    • 死角で喋っている人が誰なのかがリモート側からだと分からない
    • マイクからの距離が遠くなりがちなので声が聞こえなかったりする
  • 誰が発言しているのか、が分かりやすい状態にしておく

オフィスメンバー、1箇所に集まる必要なし

  • 全員イヤホンして自席やフリスペでそれぞれ話せば良い
  • 集まるとどうしても物理的に近い側が優位になりリモート側が隔離されるタイミングがある
  • あと、会議室をいちいち取らなくて良くなる
    • 自席などで周りに人がいる状況で独り言的に話すのは最初恥ずかしいと思うけどそのうち慣れます

喋らない時はミュートにする

  • イヤホンせずにスピーカーから音声を流している場合は特にハウリング、山彦するので常に意識してミュートを忘れないようにする
  • Zoomでは音が出ている人が分かる機能があり、それの活用にもなる

同時に喋らない

  • オフラインでの会話と違い映像・音声共に遅延が発生するので非常にコミュニケーションしづらくなる
  • 誰かが喋っている時、他メンバーはミュートにして聞くことに徹すると会話がしやすくなる
  • 交代制にしやすくするために喋り終わったら「以上です」などを付けるようにすると分かりやすい

会場は通話部屋

  • 会議室やオフィスは会場ではない
  • HangoutsやZoomに作成した通話部屋が会議の会場である、という認識を持つ
  • 画面越しの人たちも会議に参加していることを意識してコミュニケーションをとる

なるべく物理ツールを使わない

  • 共有がしづらい、デジタル化しづらいため極力避ける
  • ホワイトボードが使いたいならMiroなどを使うと良い
    • オンライン上で共同編集が可能、閲覧もそのまま出来る

共有できるものはする

  • 議事録や資料など、URLを先に伝えておいたり、画面共有したりする
  • 「聞き取れなかった」「さっきの資料を再確認したい」といったメンバーがいることを考慮する

大人数での議論は避ける

  • ROM専(聞いているだけの人)が増えるだけなので大人数集まるのは非常に効率が悪い
  • 肌感だが頑張っても8人くらいが限界だと思う
    • 少なければ少ない方が楽ではある
    • 8人に収まらないような会議はそもそも会議の内容もメンバーも分割した方がいい

喋りますの合図を決めておく

  • あると便利くらいの感じで別に必須ではない
  • 手を挙げるとか、Zoomであればリアクション機能を使うなどして合図を決めておくと誰が喋るかが分かりやすい
  • ファシリテーターがいればそれで良い場合もある

さいごに

たまたま開発部がリモートワークの検証期間中であったため、そこまで大きな混乱もなく導入、良いスタートダッシュが切れています。ですが、改善できる箇所はまだ沢山残っているので、今後も改善を続けていき今よりも高いパフォーマンスを発揮していけるようにしたいです。もし皆さんの会社でも導入しようか考えている、導入したが上手く機能していないと感じた時、この記事を読んでいただいて何か参考になるものがあれば良いなと思っています。

*1:GSuite版のHangoutsです

*2:Slack上から打刻する機能を利用しています

Swift製CLIツールをMintを使わずSwiftPMで管理する

こんにちは!エンジニアの柳村です。

SwiftLintやSwiftFormat,XcodeGenといったSwift製のコマンドラインツールを管理するために、同じくSwift製であるMintを使っているのを割とよく見かけます。 Mintは便利ですが、Mint自体をなにかしらの手段でインストールしなければならないという問題がでてきます。

そこでMintは使わずSwift Package ManagerでSwift製のコマンドラインツールを管理する方法をご紹介します。

方法

Package.swiftを用意します。 nameを適当に設定し、dependenciesに利用したいコマンドラインツールのリポジトリのurlと必要であればバージョンを指定します。

// swift-tools-version:5.1
import PackageDescription

let package = Package(
    name: "Tools",
    dependencies: [
        .package(url: "https://github.com/apple/swift-format", .branch("master")),
        .package(url: "https://github.com/yonaskolb/XcodeGen", from: "2.13.0"),
        .package(url: "https://github.com/mac-cain13/R.swift", .exact("5.1.0")),
    ]
)

あとはswift runするだけです。

$ swift run -c release swift-format --help

最初の実行時はコマンドラインツールのビルドが行われるため結構時間がかかりますが、2回目以降は.build/release以下にビルド結果が残っているのでビルドなしで実行できます。

Swift Package Managerでツール以外のパッケージも管理している場合

Swift Package Managerを使っていないプロジェクトの場合は、なにも考えずにプロジェクトのルート直下にPackage.swiftを配置しても特に問題ないと思いますが、既にSwift Package Managerを使ってる場合は対処が必要です。

なぜなら、Swift Package Managerはdebugビルドやtestのときのみのdependenciesを設定する機能がないため、dependenciesに書いたものは必ずダウンロードとビルドが実行されてしまうからです。そのためリリースビルドなどが無駄に遅くなってしまったりします。

対処方法

その1. ディレクトリを分ける

コマンドラインツール系は別のディレクトリにおいてしまうのが手っ取り早い方法です。

例えば、cliというディレクトリをプロジェクト内につくって、そのディレクトリ内にコマンドラインツールのdependenciesを書いたPackage.swiftを配置しswift runを実行すればよいです。swift run --package-path cli xcodegenというふうに--package-pathでPackage.swiftのあるディレクトリを指定すればcliディレクトリに移動せずにswift runすることができます。

その2. 別の名前のPackage.swiftを用意して実行時にPackage.swiftに置き換える

Swift Package Manager製のコマンドラインツールの開発で使われたりするやり方で、例えばPackage.test.swiftというテスト用のファイルを用意しておいて、テストするときだけPackage.test.swiftをPackage.swiftに置き換えます。

普通のiOSアプリ開発の場合は、Package.release.swiftというファイルを用意しておいて、CIなどでリリースビルドするときだけPackage.release.swiftをPackage.swiftに置き換えてやるとよいのではないかと思います。

ただ、2つのファイルをメンテしなければならないといったデメリットがあります。

Mintとの違い

最後にMintとの違いについて触れておきます。

Mintはglobalに保存

Swift Package Managerの場合はPackage.swiftのあるディレクトリの.build以下にcloneやビルド結果が保存されますが、 Mintの場合は/usr/local/lib/mint/以下に保存されます。複数のプロジェクトがあり、他のプロジェクトと同じバージョンのパッケージを使っていた場合はキャッシュが効くメリットがあります。

Mintはglobal linkつくるのが楽

MintはCLIを実行するrunだけでなく、globalにinstallするinstallという機能があります。 どいういうことかというと、例えばmint install yonaskolb/XcodeGenを実行しておくと、/usr/local/bin以下に実行ファイルのリンクがつくられるので、mint run xcodegenとしなくても、xcodegenだけで実行できるようになります。

Mintはconfig fileなしで実行できる

Swift Package Managerの場合Package.swiftを用意しなければなりませんが、Mintの場合はMintfileなしでも使えます。

まとめ

基本的にはMint使わなくてもSwift Package Managerで事足りるのではないかなと思っています。

ただ、SwiftLintの0.38.xはビルド通らないようでした(https://github.com/realm/SwiftLint/issues/2867)のでご注意ください。(0.39.0で治りました)

そしてコネヒトではエンジニアを募集しておりますので少しでも興味のある人はお話だけでも聞きにきてください! www.wantedly.com

コネヒトはPHPerKaigi 2020にプラチナスポンサーとして協賛いたします!

こんにちは。エンジニアの 高野 (@fortkle) です。
さて、今日はイベント協賛の告知をさせてください!

PHPerKaigi 2020に協賛いたします

タイトルにもある通り、PHPerKaigi 2020 にプラチナスポンサーとして協賛いたします。
コネヒトではメインプロダクトである「ママリ」を始めとして開発のメイン言語としてPHPを活用しており、フレームワークとしてはCakePHPを採用しています。

イベント概要

PHPerKaigiはどんなイベントか、公式サイトから拝借すると

PHPerKaigi(ペチパーカイギ)は、PHPer、つまり、現在PHPを使用している方、過去にPHPを使用していた方、これからPHPを使いたいと思っている方、そしてPHPが大好きな方たちが、技術的なノウハウとPHP愛を共有するためのイベントです。

という 過去・現在・未来PHPerに向けたお祭り のようです! 他のカンファレンスと同様に公募トークがありつつ、数人で特定のテーマについてディスカッションするInteractive Round Tableや、アンカンファレンスなどちょっと違ったことも企画されているとのこと!

タイムテーブルもすでに発表されているので興味がある方は要チェックです!

タイムテーブル | PHPerKaigi 2020 #phperkaigi - fortee.jp

コネヒトからは2名登壇します

@itosho 2020/02/09 16:55〜 Track A 15分トーク
登壇タイトル:「Deep Module in PHP」

fortee.jp

@fortkle 2020/02/11 15:00〜 Track A 15分トーク
登壇タイトル:「GitHub Actionsで始めるPHPアプリケーションのCI実践入門」

fortee.jp

ご参加される方はぜひ発表を聞いていただければ幸いです!

チケット購入はこちらから

phperkaigi.jp

最後に

ここまで読んでくださった皆様、ありがとうございました。
そして、 PHPerチャレンジ中の皆様、お目当てのPHPerトークンはこちらです!

#EnjoyPHPWithConnehito

それでは!

コネヒトではPHPerを積極採用中です!

Connehito Image PHPとAWSで開発!生活領域における課題を解決したいエンジニア募集!