クラウドワークス エンジニアブログ

日本最大級のクラウドソーシング「クラウドワークス」の開発の裏側をお届けするエンジニアブログ

ペアプログラミング + メンタースイッチを組み合わせてやってみた!

テーマ:ペアプロ

こんにちは、今年4月に入社した新人プログラマの@flatbaです。クラウドワークスでは、開発手法にペアプログラミングペアプロ)を取り入れています。

今回、既存メンバーが新人(僕)を新たにチームに受け入れるにあたって、

  • 既存チームへ新メンバーのスムーズな受け入れ

  • 新メンバーと既存メンバーとの早期の相互理解

  • スキルアップ

を目的としてチーム内で ペアプログラミング + メンタースイッチ を導入して実感したメリット、実践するなかで出てきた課題とその解決の様子、 を今回のブログのテーマにします。

目次

ペアプロとは

f:id:flatba:20180508105702j:plain
2人のプログラマーによるペアプログラミング

ペアプログラミング(英: pair programming)は、2人のプログラマが1台のワークステーションを使って共同でソフトウェア開発を行う手法である。一方が単体テストを打ち込んでいるときに、もう一方がそのテストを通るクラスについて考えるといったように、相補的な作業をする。 実際にキーボードを操作してコードを書く人を「ドライバ」、もう1人を「ナビゲータ」と呼ぶ。30分ごとか、単体テストを1つ完成させる度に役割を交替するのがよいとされる。また、1日に一度の頻度でパートナーを変えるのがよいともされている。 ペアプログラミング - Wikipedia

ペアプログラミング + メンタースイッチ

上記のような一般的なペアプログラミングの手法に習って、ドライバとナビゲータとに分けて2人で1つのモニタで同じタスクに取り組むという形式を取りました。 今回はそこに、技術メンタリングも組み込んで、 1週間おきにメンターを入れ替える(以下、メンタースイッチ*1)という試みに挑戦しています。

f:id:flatba:20180509123355p:plain
メンタースイッチの図

新人1人に対して、密に関わってくれる技術メンター2人という、とても贅沢な受け入れ体制です。 うちは人数が少ないからそんなに教育リソースは割けないという声も聞こえてきそうですが、この ペアプログラミング + メンタースイッチ を組み合わせることで、 むしろチーム内の教育リソースが分散されるメリットがあると思います。 また、新メンバー受け入れ時にもお互いの期待を合わせることができ、チーム全員で新人の受け入れについて考えられる様になっています。

導入してみて良かった点

受け入れの早い段階から適切にチーム内に新人のスキル感が共有されたり、教育のリソースが一極集中にならないでの集中した時間を確保してもらいやすい、同じタスクに取り組むので質問がしやすいなどのメリットを感じました。

ペアプログラミングによって得られた効果

  • 言葉では伝えきれていないスキル感を共有しやすい。
  • 質問や確認などコミュニケーションが取りやすい。
  • 一緒に作業する時間を集中的に確保すると理解が深まりやすい。

メンタースイッチによって得られた効果

  • チームメンバーに適切に新人のスキル感が共有される。
  • 新人に与える適切なタスク量をチームメンバー単位で把握しやすい。
  • メンターを個人のタスクとして持つのではなく、チームのミッションとして全員で考えられる様になった。

ペアプログラミング+メンタースイッチによって得られた効果

  • 開発効率化のためのTipsを短期間で複数人から得られるので学習効率が高くなる。
  • 教育リソースが分散されるのでまとまった時間を確保してもらいやすくなる。
  • 短期間で複数の開発の観点を得られる。

課題と解決の様子

効率を上げるために、一日の終わりに口頭ベースでお互いに思っていることを共有するミニ振り返りを行うようにしたことで、日々やり方がアップデートされるようになりました。 日々上がってくる課題は、このミニ振り返りで改善をしています。

会話例

新人: 知識的に知らないからハマっているのか、考え方が悪くてハマっているのか判断できないことがあるんですよね...。

メンター役 A先輩: Slackに個人の分報チャンネルを作って作業の状況をつぶやいてみましょうか!

=> 自分がどこでハマっているのかをメンターに把握してもらいやすくなった。

メンター役 B先輩: 説明を受けて習得する方式 or ヒント少しづつ与えて自分で考える時間多い方式のどちらが良いですか?

新人: "説明をがっつり受ける日" と "自分なりにじっくり考えてみる日" を一日おきに繰り返していくと覚えていきやすい気がします!

=> 一方的に教えられるばかりではなくて、より自分に適した教え方を聞いてもらえたことでより効果的になっていくと感じたし、もっとがんばろうと思えた。

あえてペアプログラミングと称する理由

ここまで読んでくださった方は、ただのOJTじゃないか。と思う方もいるかと思いますが、 ここで大事なことは、今回の取り組みをOJTと言わずにあえてペアプログラミングと称することに意味があると思いました。 OJTと称してしまうと、教える側 / 教えてもらう側という上下関係ができてしまって、人によっては意見が出にくくなるなど、 心理的な壁が生まれることもありますが、ペアプログラミングと言われることで、そこの垣根が低くなりました。 また、ペアプロとして一つのタスクに取り組むことで新人でも成果に繋がりやすいので達成感が得られやすく、かなりモチベーションが上がります!!

まとめ

  • お互いの期待を合わせることができ、チーム全員で新人の受け入れについて考えられる様になった。

  • 複数の価値観に触れながら開発スタイルを育むことができるので、技術の効率化(エディタやツール選定、技術の志向性など)の取捨選択を意識してするようになる。(技術に偏りが生まれにくくなる)

  • 新メンバーのチームへの受け入れが早くなる。

まとめるとこのような感じですが、 新人として ペアプログラミング+メンタースイッチ を受けている感想としては、 ペアプロを通していることで メンタリングしてもらうときに新人側のスキル不足で言語化できない課題をメンターに把握してもらいやすいとか、 質問するとかの心理的な壁が低くなるとか、 ペアプロの延長で技術的な相談もしやすくなるとか、 単純なOJTよりもチームに加わりやすいのではないか、 ということも強く実感しています! チームで成長していける環境を作っていけるのはうれしいですね!!

以上、「ペアプログラミング + メンタースイッチを組み合わせてやってみた!」でした!

クラウドソーシングのクラウドワークスでは、成長し続けたいエンジニアを募集しています!

www.wantedly.com

*1:メンタースイッチとは、 "意図的に新人のメンターを入れ替える取り組み" をチーム内でそう呼ぶようになり、自然とチーム内での共通言語として使われるようになった独自の取り組みです。

Keen IOでスマホアプリの行動分析をやってみたらとっても良かった話

こんにちは、スマホアプリチームの@tkoshidaです!

今回は弊社のスマートフォンアプリCrowdWorks for Worker iOS / Android の行動分析をKeen IOを使っていい感じにできるようになったお話を共有できればと思います。

f:id:tkoshida:20180501162913p:plain

背景

クラウドワークスアプリでは、ユーザー行動分析ツールとして初期の頃からFlurry Analyticsを採用していました。

image.png

2015年当時、無料の分析ツールとしてアプリに特化した点やその実績などから、Flurry Analyticsが一番良さそうということで採用しました。

使い勝手としてはGoogle Analyticsに似ていて、

  • DAUの確認
  • PageViewイベント、ボタンタップなどのactionイベントの利用状況確認

などをFlurry Analyticsを使って行っていました。

Flurry Analyticsはスマホアプリに特化している分なんとなく安心感のようなものもあったりして、2年以上使い続けていましたが、一方で、個人を特定したユーザ行動のトラッキングなどはできないという欠点も抱えていました。

転機

そんなとある日、Flurry Analyticsより一通のメールが...。

Your iPhone app, xxx, has just exceeded the allowed limit on the number of unique event names.You can manage the full list of event names for this app by clicking here. No additional event names will be recorded until action is taken to bring the number below the limit.

Flurry Analyticsで記録ができるPageViewやActionイベントの数には上限があって、 300を越えるイベントを記録することができない! ということらしい。 今後使い続けていくためには、重要ではないイベントを削除したりしていい感じにやりくりしていく必要が出てきました。

とはいえ、まだまだ発展途上であったクラウドワークスアプリ。今後ともどんどん機能を追加する予定はあります。当然のごとく、機能が追加されるにつれてとりたいイベントログはどんどん増えるわけです。 Flurry Analyticsのサポートに交渉して、イベント数の最大数を増やせないか調べましたが、それも無理そうでした。

以上のような経緯から、クラウドワークスではアプリのイベント計測に課題をかかえた状態となり、また普段Flurry Analyticsを使う上でできていなかったことを改善すべく、新たな分析ツールを探すこととなりました。

導入要件

同じ失敗をしないよう、新たな分析ツールの導入にあたっては要件を明確に定めました。

ユニークイベント数にしばられず使える

Flurry Analyticsはイベント数の上限:300 にかかっていました。 新たなツールでは、このような上限なく必要なイベントを必要なだけ計測できる必要があります。

ユーザーの行動が日次ベースでざっくり見られる

Flurry Analyticsで簡単に見ることができていた

  • DAU
  • 継続率

などのサマリは、新たなツールでも引き続き簡単に見られる必要があります。 (できれば日次でSlackに流せるとなおよい)

ユーザー個別で行動がトラッキングできる

Flurry Analyticsに無くて困っていた点です。

クラウドワークスでは、ユーザーさんから「〇〇の操作ができない」といったお問い合わせをいただくことがあります。 アプリでは、Webサーバーのように詳細なログが記録されていないため、どこの画面でどういったアクションをしたときに問題事象に出くわしてしまったのか、お問い合わせ内容だけでは判断がつかないことがしばしばあります。

お問い合わせされたユーザーさんのユーザーIDをもとに、行動トラッキングができれば、そのユーザーさんがどういう画面をみてどのようなアクションを行ったかを追うことができ、不具合の再現が大幅にしやすくなります。

行動分析をした結果をチーム内でURL共有できる

Flurry Analyticsでは、ユーザーフローを可視化するUser Pathsなどの機能を使って分析することがありますが、その結果をチームメンバーにURLで共有するのが難しいです。

チームのコミュニケーションはSlackでおこなっているため、できれば分析結果の共有の際にはURLをSlackに貼るだけでぱっと共有できることが望ましいです。

クラウドワークス上の既存のRDBの内容とテーブルをJOINして分析できる

「この仕事を契約したユーザが次にとっている行動、次に見ている仕事」などを抽出する際に、ユーザのメタデータを参照したくなることがあります。

Flurry Analyticsにはユーザの属性を設定する機能はありますが、アプリもしくはAPIを通してクラウドワークスのユーザデータベースの情報をFlurry Analytics側に同期する必要があるため、運用負荷が高く現実的ではありませんでした。 クラウドワークス側のRDBとINNER JOINして、ぱっとメタデータ情報で絞り込みなどができることが望ましいです。

他にどんなものが使える可能性があったか

Firebase Analytics

https://firebase.google.com/docs/analytics/?hl=ja

検討当時、AndroidアプリではすでにFirebase Analyticsを導入済みだったため、Keen IOを新規で導入するのに比べると導入コストが少し減らせるかも?ということで有力候補ではありました。

ただ、導入検討を進めていくと、Firebase Analyticsは、Flurry Analyticsと同様に、「ざっくりの傾向」をみるのには便利な一方で、個別のユーザーの行動をトラッキングしようとすると、BigQueryとの連携が必要になることがわかりました。

実際にFirebase AnalyticsをBigQuery連携させてみたものの、

  • そもそも イベント数の上限が500に制限 されていた!
  • Firebase Analyticsのデータ構造が複雑
  • クラウドワークスのRDBデータとの連携が簡単にはできなかった

などの理由から、Firebase Analytics+BigQueryは導入見送りとしました。

 

そんなとき・・・

Web側でKeen IOが採用された!

f:id:tkoshida:20180501161522p:plain

ある日のこと、Web側のチームより「ヘイユー!アプリでKeen IO使ってみない?」と話があり、次の日わたしはアプリへのKeen IOの導入に取りかかりました。

Web側でKeen IOが採用された理由は以下をご確認いただければと思います。

Keen IOによるやさしいユーザー行動ログ収集

アプリ観点では、上記リンク先でも述べられているようにiOS/AndroidSDKが存在することや、行動ログデータは最終的に弊社Redshiftへ連携しRedashから扱えるようになるため、導入要件として考えていたことがほぼ全て満たされるもので、これを採用しない手はありませんでした。

iOS/Androidによる行動ログ計測

Keen IOのiOS SDKこちらを、Android SDKこちらをご確認ください。 またKeen IOのドキュメントはこちらをご確認ください。

Event Collection

Keen IOでは、Event Collectionというイベントをとりまとめるための種別があり、アプリでどういうEvent Collectionにするかをまず決めました。

たとえばWeb側ではWeb Auto-Collectionという自動でいい感じにページの表示などを計測してくれる仕組みがあり、それを利用すると

  • pageviews -- ページビュー
  • clicks -- クリック (ボタンやリンクだけでなくすべてのクリック)
  • form_submissions -- フォーム送信 (送信された内容も含む)

といったEvent Collectionが作られ、それぞれにページの表示イベントなどを自動でとりまとめて記録されますのでアプリでも参考に以下のように分けてみました。

  • ios_views -- iOSアプリでの画面表示イベント
  • android_views -- Androidアプリでの画面表示イベント
  • ios_actions -- iOSアプリでのボタンタップなどのアクションイベント
  • android_actions -- Androidアプリでのボタンタップなどのアクションイベント

ちなみに、弊社ではこのEvent Collectionごとに最終的にはRedshiftにテーブルを作ってデータ連携するようにしています。 この分け方については実際に運用してみてRedashからクエリを叩く時にテーブルが別れていて面倒、などチーム内でも議論がありますので、今後見直す可能性があります。

アプリからはこのviewsイベントおよびactionsイベントの計測については以下のようなメソッドを用意しています。 以降、Swiftを例に説明します。

/// viewイベント計測
static func addViewEvent(name: String, params: [String: Any]?) {
    let event: [String: Any] = [
        "unixtime": Int(Date().timeIntervalSince1970),
        "session_uuid": sessionUuid(),
        "view": [
            "name": name,
            "params": params ?? [:]
        ]
    ]
    do {
        try KeenClient.shared().addEvent(event, toEventCollection: "ios_views")
    } catch _ {
        // do something
    }
}

/// actionイベント計測
static func addActionEvent(name: String, params: [String: Any]?) {
    let event: [String: Any] = [
        "unixtime": Int(Date().timeIntervalSince1970),
        "session_uuid": sessionUuid(),
        "action": [
            "name": name,
            "params": params ?? [:]
        ]
    ]
    do {
        try KeenClient.shared().addEvent(event, toEventCollection: "ios_actions")
    } catch _ {
        // do something
    }
}

両メソッドとも引数にnameを指定しますが、ここに画面名やアクション名を渡すようにします。 paramsには必要に応じてパラメータを渡して同時に計測するようにしています。

また、unixtimesession_uuidも記録しています。 unixtimeについては端末時刻を単純にエポック秒で送付しているのみですが、Keen IO側の受信時間ではなく端末で実際にいつイベントが発生したのかを送るようにしています。ただこれは端末時間がずれていたりする懸念があります。 session_uuidに関してはWebのセッションのようなユーザーの操作単位として、30分間操作の間隔があかない操作が続く間は1セッションとして同一IDをふって、そのIDで絞り込むことによりユーザーの一連の操作がどういったものかを把握できるようにしています。

例えば画面表示のイベントについては、viewDidLoad:などのタイミングで上記メソッドを呼んでやるとKeen IOに対してイベントを飛ばすこととなります。 上記のメソッドなどKeen IOに関するコードは KeenIOServiceというクラスに処理をまとめているためそのクラスに対するメソッド呼び出しになっています。

override func viewDidLoad() {
    super.viewDidLoad()
    KeenIOService.addViewEvent(name: "Hogehoge", params: ["jobOfferId" : 100])
}

Enrichment addon

上記のイベント処理だけでは単純にイベント名やそのパラメータの情報くらいしかないため、もっと情報を追加したいと思います。 Web側ではWeb Auto-Collectionという自動で色々な情報を収集してくれる仕組みがあり、さらにIPアドレスからGeo情報を出してくれたりと色々してくれますが、アプリで同様の機能がないため独自に計測するイベント決める必要があります。

Keen IOにはそのIPアドレスからGeo情報を出してくれたりする便利な機能をEnrichment addonとして個別に提供してくれています。

アプリからKeen IOに送るイベントに以下のように

KeenClient.shared().globalPropertiesDictionary = [
    "ip_address": "${keen.ip}",
    "keen": [
        "addons": [
            [
                "name": "keen:ip_to_geo",
                "input": [
                    "ip": "ip_address"
                ],
                "output": "geo"
            ],
            ...
        ],
        ...
]

addonskeen:ip_to_geo の指定を追加してやると

"geo": {
    "province": "Tokyo",
    "city": "Tokyo",
    "country": "Japan",
    "coordinates": [
    139.7677,
    35.6427
    ],
    "postal_code": "100-0001",
    "country_code": "JP",
    "continent": "Asia"
},

このようにイベントに対して位置情報をあわせて記録してくれるようになります。

Enrichment addonには他にも keen:date_time_parser を指定するとKeen IOがイベントを受信した時刻を出力してくれるものがあり、アプリで使えそうでしたのでその指定も追加しました。

Global properties

Enrichment addonで取れないものはアプリで独自にイベント情報のプロパティに追加する必要があります。 たとえば、ユーザー情報、デバイス情報などです。

こちらも以下のように追加します。 DeviceHelperなどは弊社で作成したヘルパ用クラスです。

KeenClient.shared().globalPropertiesDictionary = [
    ...
    "user": [
        "logged_in": isLoggedIn,    // ログイン状態
        "user_id":  userId,    // ユーザーID
    ],
    "device": [
        "carrier_name": DeviceHelper.carrierName(),
        "os": "ios",
        "os_version": AppVersionHelper.systemVersion,
        "app_version": AppVersionHelper.versionName,
        "build_version": AppVersionHelper.buildVersion,
        "locale": Locale.current.identifier,
        "model": DeviceHelper.modelName(),
        "device_id": KeychainManager.sharedInstance.deviceId,
    ],
    "tracking_metadata": [
        "version": trackingVersion,
    ],
]

ちなみに、tracking_metadata/version というものも計測していますが、アプリから送信するこのJSON構造はアプリエンハンスにしたがって変わりうるものであるため、構造の変化に対応できるようにversion付けするようにしたものです。

この結果、最終的にアプリから送るイベントのJSON構造は次のようなものになりました。

{
  "tracking_metadata": {
    "version": "2018021900"
  },
  "keen": {
    "timestamp": "2018-04-26T08:20:58.333Z",
    "created_at": "2018-04-26T08:20:57.829Z",
    "id": "1234567890abc"
  },
  "geo": {
    "province": "Tokyo",
    "city": "Tokyo",
    "country": "Japan",
    "coordinates": [
      139.692101,
      35.689634
    ],
    "postal_code": "163-8001",
    "country_code": "JP",
    "continent": "Asia"
  },
  "unixtime": 1524730858,
  "device": {
    "locale": "ja_JP",
    "os_version": "7.0",
    "build_version": 2018042000,
    "carrier_name": "NTT DOCOMO",
    "app_version": "2.28.3",
    "os": "android",
    "model": "SH-02J",
    "device_id": "12345678-1234-1234-1234-1234567890ab"
  },
  "user": {
    "logged_in": true,
    "user_id": 1234567890
  },
  "session_uuid": "87654321-4321-4321-4321-ba0987654321",
  "time": {
    "utc": {
      "millisecond": 333,
      "day_of_week_string": "Thursday",
      "hour": 17,
      "timezone_offset": 32400000,
      "day_of_month": 26,
      "day_of_week": 4,
      "day_of_year": 116,
      "second": 58,
      "week": 17,
      "year": 2018,
      "month": 4,
      "minute": 20,
      "quarter_of_year": 2
    }
  },
  "action": {
    "params": {
      "row": "0",
      "relatedJobOfferType": "otherViewing"
    },
    "name": "JobOffer"
  },
  "ip_address": "10.0.0.1"
}

Redashで収集したデータをSQLで分析

こうして集めたKeen IOのデータは弊社で管理するRedshiftにデータ連携させ、最終的にRedashでテーブルとして参照できるようになっています。

この結果、Redashでこんな感じで、アプリの行動ログを眺めることができました!

image.png

チーム内全員でアプリの行動分析

せっかく行動ログの分析がいい感じにできるようになりましたので、最近アプリ開発チーム内でRedashからKeen IOで収集したデータを分析してみるハンズオンを行い、チームメンバー全員でアプリ内の行動分析を行えるようになりました。

このハンズオンもチーム内では好評で、「自分でも分析できるようになれた!」と喜びの声があがっています。 今後積極的に行動分析からサービスの改善に繋げていければと思います。

結果

これまで説明してきたようにKeen IOを導入してみて、いくつか見えてきたことがありますので、良かった点や課題となっている点をまとめてみたいと思います。

良かったこと

これまでの普段の分析については、RDBにたまったお仕事や支払いの情報などをRedshiftに連携しそれをRedashでSQLにて分析していました。 それがアプリの行動ログについてもKeen IOからRedash連携ができるので、行動分析がいつもの分析と遜色なく行うことができるようになりました。 SQLを使って分析する人が多い弊社において、この点は非常に大きなポイントでした。 その結果、ユーザーさんの困っていること、置かれている現状などを詳細に把握できるようになってきており、解決へのアクションにつなげられるようになりました。

課題、反省点

テーブルの分け方は事前に確認することをおすすめします。 今回、私たちはviewsとactionsでイベントを分けてデータ連携しましたが、分析の内容によってはviewsとactionsを毎回JOINするという工数が発生してしまいました。 しかもiOS, Androidでも分かれているので4つのテーブルを必ず毎回JOINするなど面倒です。 Event Collectionはviews/actionsで分けているのは良いかもしれませんが、Redshiftへデータ連携する場合には、views/actionsを一つのテーブルにしてtypeなどのカラムで判定してやるのが良かったかもしれません。

また、ここでは詳しく述べませんが、Keen IOからRedshiftに連携するまでにデータの加工などを行っています。また扱うデータの量が大きすぎて、うまく扱うためには色々な工夫が必要で大変でした。 機会があればこちらについても別途ご紹介できればと思います。

最後に

いかがでしたでしょうか。 導入にあたっての準備がいくつか必要とはいえ、Keen IOの導入によって分析の幅は圧倒的に広がりますので、ぜひお勧めしたいと思います。

現在、クラウドワークス では積極的にアプリ開発に関わるエンジニアを募集しています。 話を聞いてみたい、などでも構いませんので、お気軽に遊びにきてくださいねー。 www.wantedly.com

TDD(テスト駆動開発)+モブプログラミングを社内でやってみた話

こんにちは!アニメとゲームが大好きな@mayoxtunaです。

2018年3月26日に入社しました。

まだ2週間ほどしか経っていませんが非常に密度が高い時間を過ごしています。

CrowdWorksのエンジニア達は積極的に社内勉強会を開催しています。

続きを読む

Terraformプロバイダから動的に型定義情報を取得するtfschemaというツールを作った

Terraform職人の @minamijoyo です。

クラウドワークスではAWSのインフラ構成管理にHashiCorpの Terraform を利用しており、 日々Terraformの設定ファイルを書いてるわけですが、 コード書いてると、リソースタイプの名前がうろ覚えとか、属性値の名前のスペルに自信がないとか、この属性値って必須項目だっけ? とか、なんだかんだで公式ドキュメントを見ながらコードを書いてることが多いです。

Vimのプラグインで補完してくれるやつがあるのは知ってるんだけど、 あらかじめリストファイルを持っているアプローチだと、バージョンに合わせてリストファイルの更新しないといけなくて、仕組み上の限界があるし、サポートされてない自作プロバイダとかだとそもそも使えない。

なんかもっといいかんじにできないかなぁと思って、趣味でTerraformのソースコードを読んでたら、最近入ったGetSchema APIというのを見つけて、欲しかったのはこれだよ感で早速試してみたらバグってるし。まじかよ。で、バグ報告修正に協力したり、AWSプロバイダにも修正を取り込んでもらったり、なんやかんやあって、結果的にTerraformプロバイダから動的に型定義情報を取得するtfschemaという俺得ツールができたので、テラフォーマーズの皆さんの役に立つかなーと思って紹介します。

github.com

続きを読む

grpc-gatewayを使用したマイクロサービスの管理画面

SREチームの那須です。

3/7に開催されたピクスタさんの 大規模プラットフォームを支えるエンジニアの技術と工夫〜Web現場Meetup #3〜 で登壇させていただきました。そのときにお話ししたgrpc-gatewayを使った管理画面の構築について改めてまとめてみます

続きを読む

© 2016 CrowdWorks, Inc., All rights reserved.